Replace factory functions with throwing constructors

This commit is contained in:
binary1248 2024-07-06 04:24:00 +02:00 committed by Chris Thrasher
parent 698f265277
commit e185f6d53e
67 changed files with 2360 additions and 2263 deletions

View File

@ -21,15 +21,15 @@
/// sf::RenderWindow window(sf::VideoMode({800, 600}), "SFML window"); /// sf::RenderWindow window(sf::VideoMode({800, 600}), "SFML window");
/// ///
/// // Load a sprite to display /// // Load a sprite to display
/// const auto texture = sf::Texture::loadFromFile("cute_image.jpg").value(); /// const sf::Texture texture("cute_image.jpg");
/// sf::Sprite sprite(texture); /// sf::Sprite sprite(texture);
/// ///
/// // Create a graphical text to display /// // Create a graphical text to display
/// const auto font = sf::Font::openFromFile("arial.ttf").value(); /// const sf::Font font("arial.ttf");
/// sf::Text text(font, "Hello SFML", 50); /// sf::Text text(font, "Hello SFML", 50);
/// ///
/// // Load a music to play /// // Load a music to play
/// auto music = sf::Music::openFromFile("nice_music.ogg").value(); /// sf::Music music("nice_music.ogg");
/// ///
/// // Play the music /// // Play the music
/// music.play(); /// music.play();

View File

@ -86,13 +86,13 @@ int main(int argc, char* argv[])
sf::RenderWindow window(screen, ""); sf::RenderWindow window(screen, "");
window.setFramerateLimit(30); window.setFramerateLimit(30);
const auto texture = sf::Texture::createFromFile("image.png").value(); const sf::Texture texture("image.png");
sf::Sprite image(texture); sf::Sprite image(texture);
image.setPosition(sf::Vector2f(screen.size) / 2.f); image.setPosition(sf::Vector2f(screen.size) / 2.f);
image.setOrigin(sf::Vector2f(texture.getSize()) / 2.f); image.setOrigin(sf::Vector2f(texture.getSize()) / 2.f);
const auto font = sf::Font::createFromFile("tuffy.ttf").value(); const sf::Font font("tuffy.ttf");
sf::Text text(font, "Tap anywhere to move the logo.", 64); sf::Text text(font, "Tap anywhere to move the logo.", 64);
text.setFillColor(sf::Color::Black); text.setFillColor(sf::Color::Black);

View File

@ -52,9 +52,9 @@ struct SFMLmainWindow
std::filesystem::path resPath{[[[NSBundle mainBundle] resourcePath] tostdstring]}; std::filesystem::path resPath{[[[NSBundle mainBundle] resourcePath] tostdstring]};
sf::RenderWindow renderWindow; sf::RenderWindow renderWindow;
sf::Font font{sf::Font::createFromFile(resPath / "tuffy.ttf").value()}; sf::Font font{resPath / "tuffy.ttf"};
sf::Text text{font}; sf::Text text{font};
sf::Texture logo{sf::Texture::createFromFile(resPath / "logo.png").value()}; sf::Texture logo{resPath / "logo.png"};
sf::Sprite sprite{logo}; sf::Sprite sprite{logo};
sf::Color background{sf::Color::Blue}; sf::Color background{sf::Color::Blue};
}; };

View File

@ -316,7 +316,7 @@ private:
// Member data // Member data
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
sf::RenderWindow m_window{sf::VideoMode({800u, 600u}), "SFML Event Handling", sf::Style::Titlebar | sf::Style::Close}; sf::RenderWindow m_window{sf::VideoMode({800u, 600u}), "SFML Event Handling", sf::Style::Titlebar | sf::Style::Close};
const sf::Font m_font{sf::Font::createFromFile("resources/tuffy.ttf").value()}; const sf::Font m_font{"resources/tuffy.ttf"};
sf::Text m_logText{m_font, "", 20}; sf::Text m_logText{m_font, "", 20};
sf::Text m_handlerText{m_font, "Current Handler: Classic", 24}; sf::Text m_handlerText{m_font, "Current Handler: Classic", 24};
sf::Text m_instructions{m_font, "Press Enter to change handler type", 24}; sf::Text m_instructions{m_font, "Press Enter to change handler type", 24};

View File

@ -91,12 +91,12 @@ int main()
sf::RenderWindow window(sf::VideoMode({windowWidth, windowHeight}), "SFML Island", sf::Style::Titlebar | sf::Style::Close); sf::RenderWindow window(sf::VideoMode({windowWidth, windowHeight}), "SFML Island", sf::Style::Titlebar | sf::Style::Close);
window.setVerticalSyncEnabled(true); window.setVerticalSyncEnabled(true);
const auto font = sf::Font::createFromFile("resources/tuffy.ttf").value(); const sf::Font font("resources/tuffy.ttf");
// Create all of our graphics resources // Create all of our graphics resources
sf::Text hudText(font); sf::Text hudText(font);
sf::Text statusText(font); sf::Text statusText(font);
std::optional<sf::Shader> terrainShader; sf::Shader terrainShader;
sf::RenderStates terrainStates; sf::RenderStates terrainStates;
sf::VertexBuffer terrain(sf::PrimitiveType::Triangles, sf::VertexBuffer::Usage::Static); sf::VertexBuffer terrain(sf::PrimitiveType::Triangles, sf::VertexBuffer::Usage::Static);
@ -120,7 +120,7 @@ int main()
{ {
statusText.setString("Shaders and/or Vertex Buffers Unsupported"); statusText.setString("Shaders and/or Vertex Buffers Unsupported");
} }
else if (!(terrainShader = sf::Shader::createFromFile("resources/terrain.vert", "resources/terrain.frag"))) else if (!terrainShader.loadFromFile("resources/terrain.vert", "resources/terrain.frag"))
{ {
statusText.setString("Failed to load shader program"); statusText.setString("Failed to load shader program");
} }
@ -148,7 +148,7 @@ int main()
statusText.setString("Generating Terrain..."); statusText.setString("Generating Terrain...");
// Set up the render states // Set up the render states
terrainStates = sf::RenderStates(&*terrainShader); terrainStates = sf::RenderStates(&terrainShader);
} }
// Center the status text // Center the status text
@ -186,7 +186,8 @@ int main()
} }
// Arrow key pressed: // Arrow key pressed:
if (terrainShader.has_value() && event->is<sf::Event::KeyPressed>()) // TODO Replace use of getNativeHandle() when validity function is added
if (terrainShader.getNativeHandle() != 0 && event->is<sf::Event::KeyPressed>())
{ {
switch (event->getIf<sf::Event::KeyPressed>()->code) switch (event->getIf<sf::Event::KeyPressed>()->code)
{ {
@ -216,7 +217,7 @@ int main()
window.draw(statusText); window.draw(statusText);
if (terrainShader.has_value()) if (terrainShader.getNativeHandle() != 0)
{ {
{ {
const std::lock_guard lock(workQueueMutex); const std::lock_guard lock(workQueueMutex);
@ -236,7 +237,7 @@ int main()
bufferUploadPending = false; bufferUploadPending = false;
} }
terrainShader->setUniform("lightFactor", lightFactor); terrainShader.setUniform("lightFactor", lightFactor);
window.draw(terrain, terrainStates); window.draw(terrain, terrainStates);
} }
} }

View File

@ -94,7 +94,7 @@ int main()
window.setVerticalSyncEnabled(true); window.setVerticalSyncEnabled(true);
// Open the text font // Open the text font
const auto font = sf::Font::createFromFile("resources/tuffy.ttf").value(); const sf::Font font("resources/tuffy.ttf");
// Set up our string conversion parameters // Set up our string conversion parameters
sstr.precision(2); sstr.precision(2);

View File

@ -58,11 +58,11 @@ int main()
window.setMaximumSize(sf::Vector2u(1200, 900)); window.setMaximumSize(sf::Vector2u(1200, 900));
// Create a sprite for the background // Create a sprite for the background
const auto backgroundTexture = sf::Texture::createFromFile(resourcesDir() / "background.jpg", sRgb).value(); const sf::Texture backgroundTexture(resourcesDir() / "background.jpg", sRgb);
const sf::Sprite background(backgroundTexture); const sf::Sprite background(backgroundTexture);
// Create some text to draw on top of our OpenGL object // Create some text to draw on top of our OpenGL object
const auto font = sf::Font::createFromFile(resourcesDir() / "tuffy.ttf").value(); const sf::Font font(resourcesDir() / "tuffy.ttf");
sf::Text text(font, "SFML / OpenGL demo"); sf::Text text(font, "SFML / OpenGL demo");
sf::Text sRgbInstructions(font, "Press space to toggle sRGB conversion"); sf::Text sRgbInstructions(font, "Press space to toggle sRGB conversion");
@ -75,7 +75,7 @@ int main()
mipmapInstructions.setPosition({200.f, 550.f}); mipmapInstructions.setPosition({200.f, 550.f});
// Load a texture to apply to our 3D cube // Load a texture to apply to our 3D cube
auto texture = sf::Texture::createFromFile(resourcesDir() / "logo.png").value(); sf::Texture texture(resourcesDir() / "logo.png");
// Attempt to generate a mipmap for our cube texture // Attempt to generate a mipmap for our cube texture
// We don't check the return value here since // We don't check the return value here since
@ -219,7 +219,7 @@ int main()
if (mipmapEnabled) if (mipmapEnabled)
{ {
// We simply reload the texture to disable mipmapping // We simply reload the texture to disable mipmapping
texture = sf::Texture::createFromFile(resourcesDir() / "logo.png").value(); texture = sf::Texture(resourcesDir() / "logo.png");
// Rebind the texture // Rebind the texture
sf::Texture::bind(&texture); sf::Texture::bind(&texture);

View File

@ -19,7 +19,7 @@ int main()
window.setVerticalSyncEnabled(true); window.setVerticalSyncEnabled(true);
// Open the application font // Open the application font
const auto font = sf::Font::createFromFile("resources/tuffy.ttf").value(); const sf::Font font("resources/tuffy.ttf");
// Create the mouse position text // Create the mouse position text
sf::Text mousePosition(font, "", 20); sf::Text mousePosition(font, "", 20);

View File

@ -278,69 +278,66 @@ private:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::optional<Pixelate> tryLoadPixelate() std::optional<Pixelate> tryLoadPixelate()
{ {
auto texture = sf::Texture::createFromFile("resources/background.jpg"); sf::Texture texture;
if (!texture.has_value()) if (!texture.loadFromFile("resources/background.jpg"))
return std::nullopt; return std::nullopt;
auto shader = sf::Shader::createFromFile("resources/pixelate.frag", sf::Shader::Type::Fragment); sf::Shader shader;
if (!shader.has_value()) if (!shader.loadFromFile("resources/pixelate.frag", sf::Shader::Type::Fragment))
return std::nullopt; return std::nullopt;
return std::make_optional<Pixelate>(std::move(*texture), std::move(*shader)); return std::make_optional<Pixelate>(std::move(texture), std::move(shader));
} }
std::optional<WaveBlur> tryLoadWaveBlur(const sf::Font& font) std::optional<WaveBlur> tryLoadWaveBlur(const sf::Font& font)
{ {
auto shader = sf::Shader::createFromFile("resources/wave.vert", "resources/blur.frag"); sf::Shader shader;
if (!shader.has_value()) if (!shader.loadFromFile("resources/wave.vert", "resources/blur.frag"))
return std::nullopt; return std::nullopt;
return std::make_optional<WaveBlur>(font, std::move(*shader)); return std::make_optional<WaveBlur>(font, std::move(shader));
} }
std::optional<StormBlink> tryLoadStormBlink() std::optional<StormBlink> tryLoadStormBlink()
{ {
auto shader = sf::Shader::createFromFile("resources/storm.vert", "resources/blink.frag"); sf::Shader shader;
if (!shader.has_value()) if (!shader.loadFromFile("resources/storm.vert", "resources/blink.frag"))
return std::nullopt; return std::nullopt;
return std::make_optional<StormBlink>(std::move(*shader)); return std::make_optional<StormBlink>(std::move(shader));
} }
std::optional<Edge> tryLoadEdge() std::optional<Edge> tryLoadEdge()
{ {
// Create the off-screen surface // Create the off-screen surface
auto surface = sf::RenderTexture::create({800, 600}); sf::RenderTexture surface;
if (!surface.has_value()) if (!surface.resize({800, 600}))
return std::nullopt; return std::nullopt;
surface->setSmooth(true); surface.setSmooth(true);
// Load the background texture // Load the background texture
auto backgroundTexture = sf::Texture::createFromFile("resources/sfml.png"); sf::Texture backgroundTexture;
if (!backgroundTexture.has_value()) if (!backgroundTexture.loadFromFile("resources/sfml.png"))
return std::nullopt; return std::nullopt;
backgroundTexture->setSmooth(true); backgroundTexture.setSmooth(true);
// Load the entity texture // Load the entity texture
auto entityTexture = sf::Texture::createFromFile("resources/devices.png"); sf::Texture entityTexture;
if (!entityTexture.has_value()) if (!entityTexture.loadFromFile("resources/devices.png"))
return std::nullopt; return std::nullopt;
entityTexture->setSmooth(true); entityTexture.setSmooth(true);
// Load the shader // Load the shader
auto shader = sf::Shader::createFromFile("resources/edge.frag", sf::Shader::Type::Fragment); sf::Shader shader;
if (!shader.has_value()) if (!shader.loadFromFile("resources/edge.frag", sf::Shader::Type::Fragment))
return std::nullopt; return std::nullopt;
shader->setUniform("texture", sf::Shader::CurrentTexture); shader.setUniform("texture", sf::Shader::CurrentTexture);
return std::make_optional<Edge>(std::move(*surface), return std::make_optional<Edge>(std::move(surface), std::move(backgroundTexture), std::move(entityTexture), std::move(shader));
std::move(*backgroundTexture),
std::move(*entityTexture),
std::move(*shader));
} }
std::optional<Geometry> tryLoadGeometry() std::optional<Geometry> tryLoadGeometry()
@ -350,25 +347,23 @@ std::optional<Geometry> tryLoadGeometry()
return std::nullopt; return std::nullopt;
// Load the logo texture // Load the logo texture
auto logoTexture = sf::Texture::createFromFile("resources/logo.png"); sf::Texture logoTexture;
if (!logoTexture.has_value()) if (!logoTexture.loadFromFile("resources/logo.png"))
return std::nullopt; return std::nullopt;
logoTexture->setSmooth(true); logoTexture.setSmooth(true);
// Load the shader // Load the shader
auto shader = sf::Shader::createFromFile("resources/billboard.vert", sf::Shader shader;
"resources/billboard.geom", if (!shader.loadFromFile("resources/billboard.vert", "resources/billboard.geom", "resources/billboard.frag"))
"resources/billboard.frag");
if (!shader.has_value())
return std::nullopt; return std::nullopt;
shader->setUniform("texture", sf::Shader::CurrentTexture); shader.setUniform("texture", sf::Shader::CurrentTexture);
// Set the render resolution (used for proper scaling) // Set the render resolution (used for proper scaling)
shader->setUniform("resolution", sf::Vector2f(800, 600)); shader.setUniform("resolution", sf::Vector2f(800, 600));
return std::make_optional<Geometry>(std::move(*logoTexture), std::move(*shader)); return std::make_optional<Geometry>(std::move(logoTexture), std::move(shader));
} }
} // namespace } // namespace
@ -394,7 +389,7 @@ int main()
window.setVerticalSyncEnabled(true); window.setVerticalSyncEnabled(true);
// Open the application font // Open the application font
const auto font = sf::Font::createFromFile("resources/tuffy.ttf").value(); const sf::Font font("resources/tuffy.ttf");
// Create the effects // Create the effects
std::optional pixelateEffect = tryLoadPixelate(); std::optional pixelateEffect = tryLoadPixelate();
@ -418,7 +413,7 @@ int main()
std::size_t current = 0; std::size_t current = 0;
// Create the messages background // Create the messages background
const auto textBackgroundTexture = sf::Texture::createFromFile("resources/text-background.png").value(); const sf::Texture textBackgroundTexture("resources/text-background.png");
sf::Sprite textBackground(textBackgroundTexture); sf::Sprite textBackground(textBackgroundTexture);
textBackground.setPosition({0.f, 520.f}); textBackground.setPosition({0.f, 520.f});
textBackground.setColor(sf::Color(255, 255, 255, 200)); textBackground.setColor(sf::Color(255, 255, 255, 200));

View File

@ -13,7 +13,7 @@
void playSound() void playSound()
{ {
// Load a sound buffer from a wav file // Load a sound buffer from a wav file
const auto buffer = sf::SoundBuffer::createFromFile("resources/killdeer.wav").value(); const sf::SoundBuffer buffer("resources/killdeer.wav");
// Display sound information // Display sound information
std::cout << "killdeer.wav:" << '\n' std::cout << "killdeer.wav:" << '\n'
@ -46,7 +46,7 @@ void playSound()
void playMusic(const std::filesystem::path& filename) void playMusic(const std::filesystem::path& filename)
{ {
// Load an ogg music file // Load an ogg music file
auto music = sf::Music::createFromFile("resources" / filename).value(); sf::Music music("resources" / filename);
// Display music information // Display music information
std::cout << filename << ":" << '\n' std::cout << filename << ":" << '\n'

View File

@ -115,23 +115,23 @@ public:
m_listener.setFillColor(sf::Color::Red); m_listener.setFillColor(sf::Color::Red);
// Load the music file // Load the music file
if (!(m_music = sf::Music::createFromFile(resourcesDir() / "doodle_pop.ogg"))) if (!m_music.openFromFile(resourcesDir() / "doodle_pop.ogg"))
{ {
std::cerr << "Failed to load " << (resourcesDir() / "doodle_pop.ogg").string() << std::endl; std::cerr << "Failed to load " << (resourcesDir() / "doodle_pop.ogg").string() << std::endl;
std::abort(); std::abort();
} }
// Set the music to loop // Set the music to loop
m_music->setLoop(true); m_music.setLoop(true);
// Set attenuation to a nice value // Set attenuation to a nice value
m_music->setAttenuation(0.04f); m_music.setAttenuation(0.04f);
} }
void onUpdate(float /*time*/, float x, float y) override void onUpdate(float /*time*/, float x, float y) override
{ {
m_position = {windowWidth * x - 10.f, windowHeight * y - 10.f}; m_position = {windowWidth * x - 10.f, windowHeight * y - 10.f};
m_music->setPosition({m_position.x, m_position.y, 0.f}); m_music.setPosition({m_position.x, m_position.y, 0.f});
} }
void onDraw(sf::RenderTarget& target, sf::RenderStates states) const override void onDraw(sf::RenderTarget& target, sf::RenderStates states) const override
@ -149,19 +149,19 @@ public:
// Synchronize listener audio position with graphical position // Synchronize listener audio position with graphical position
sf::Listener::setPosition({m_listener.getPosition().x, m_listener.getPosition().y, 0.f}); sf::Listener::setPosition({m_listener.getPosition().x, m_listener.getPosition().y, 0.f});
m_music->play(); m_music.play();
} }
void onStop() override void onStop() override
{ {
m_music->stop(); m_music.stop();
} }
private: private:
sf::CircleShape m_listener{20.f}; sf::CircleShape m_listener{20.f};
sf::CircleShape m_soundShape{20.f}; sf::CircleShape m_soundShape{20.f};
sf::Vector2f m_position; sf::Vector2f m_position;
std::optional<sf::Music> m_music; sf::Music m_music;
}; };
@ -177,23 +177,23 @@ public:
m_volumeText(getFont(), "Volume: " + std::to_string(m_volume)) m_volumeText(getFont(), "Volume: " + std::to_string(m_volume))
{ {
// Load the music file // Load the music file
if (!(m_music = sf::Music::createFromFile(resourcesDir() / "doodle_pop.ogg"))) if (!m_music.openFromFile(resourcesDir() / "doodle_pop.ogg"))
{ {
std::cerr << "Failed to load " << (resourcesDir() / "doodle_pop.ogg").string() << std::endl; std::cerr << "Failed to load " << (resourcesDir() / "doodle_pop.ogg").string() << std::endl;
std::abort(); std::abort();
} }
// Set the music to loop // Set the music to loop
m_music->setLoop(true); m_music.setLoop(true);
// We don't care about attenuation in this effect // We don't care about attenuation in this effect
m_music->setAttenuation(0.f); m_music.setAttenuation(0.f);
// Set initial pitch // Set initial pitch
m_music->setPitch(m_pitch); m_music.setPitch(m_pitch);
// Set initial volume // Set initial volume
m_music->setVolume(m_volume); m_music.setVolume(m_volume);
m_pitchText.setPosition({windowWidth / 2.f - 120.f, windowHeight / 2.f - 80.f}); m_pitchText.setPosition({windowWidth / 2.f - 120.f, windowHeight / 2.f - 80.f});
m_volumeText.setPosition({windowWidth / 2.f - 120.f, windowHeight / 2.f - 30.f}); m_volumeText.setPosition({windowWidth / 2.f - 120.f, windowHeight / 2.f - 30.f});
@ -204,8 +204,8 @@ public:
m_pitch = std::clamp(2.f * x, 0.f, 2.f); m_pitch = std::clamp(2.f * x, 0.f, 2.f);
m_volume = std::clamp(100.f * (1.f - y), 0.f, 100.f); m_volume = std::clamp(100.f * (1.f - y), 0.f, 100.f);
m_music->setPitch(m_pitch); m_music.setPitch(m_pitch);
m_music->setVolume(m_volume); m_music.setVolume(m_volume);
m_pitchText.setString("Pitch: " + std::to_string(m_pitch)); m_pitchText.setString("Pitch: " + std::to_string(m_pitch));
m_volumeText.setString("Volume: " + std::to_string(m_volume)); m_volumeText.setString("Volume: " + std::to_string(m_volume));
@ -223,12 +223,12 @@ public:
// so that the music is right on top of the listener // so that the music is right on top of the listener
sf::Listener::setPosition({0.f, 0.f, 0.f}); sf::Listener::setPosition({0.f, 0.f, 0.f});
m_music->play(); m_music.play();
} }
void onStop() override void onStop() override
{ {
m_music->stop(); m_music.stop();
} }
private: private:
@ -236,7 +236,7 @@ private:
float m_volume{100.f}; float m_volume{100.f};
sf::Text m_pitchText; sf::Text m_pitchText;
sf::Text m_volumeText; sf::Text m_volumeText;
std::optional<sf::Music> m_music; sf::Music m_music;
}; };
@ -283,23 +283,23 @@ public:
makeCone(m_soundConeInner, innerConeAngle); makeCone(m_soundConeInner, innerConeAngle);
// Load the music file // Load the music file
if (!(m_music = sf::Music::createFromFile(resourcesDir() / "doodle_pop.ogg"))) if (!m_music.openFromFile(resourcesDir() / "doodle_pop.ogg"))
{ {
std::cerr << "Failed to load " << (resourcesDir() / "doodle_pop.ogg").string() << std::endl; std::cerr << "Failed to load " << (resourcesDir() / "doodle_pop.ogg").string() << std::endl;
std::abort(); std::abort();
} }
// Set the music to loop // Set the music to loop
m_music->setLoop(true); m_music.setLoop(true);
// Set attenuation factor // Set attenuation factor
m_music->setAttenuation(m_attenuation); m_music.setAttenuation(m_attenuation);
// Set direction to face "downwards" // Set direction to face "downwards"
m_music->setDirection({0.f, 1.f, 0.f}); m_music.setDirection({0.f, 1.f, 0.f});
// Set cone // Set cone
m_music->setCone({innerConeAngle, outerConeAngle, 0.f}); m_music.setCone({innerConeAngle, outerConeAngle, 0.f});
m_text.setString( m_text.setString(
"Attenuation factor dampens full volume of sound while within inner cone based on distance to " "Attenuation factor dampens full volume of sound while within inner cone based on distance to "
@ -314,7 +314,7 @@ public:
void onUpdate(float /*time*/, float x, float y) override void onUpdate(float /*time*/, float x, float y) override
{ {
m_position = {windowWidth * x - 10.f, windowHeight * y - 10.f}; m_position = {windowWidth * x - 10.f, windowHeight * y - 10.f};
m_music->setPosition({m_position.x, m_position.y, 0.f}); m_music.setPosition({m_position.x, m_position.y, 0.f});
} }
void onDraw(sf::RenderTarget& target, sf::RenderStates states) const override void onDraw(sf::RenderTarget& target, sf::RenderStates states) const override
@ -336,12 +336,12 @@ public:
// Synchronize listener audio position with graphical position // Synchronize listener audio position with graphical position
sf::Listener::setPosition({m_listener.getPosition().x, m_listener.getPosition().y, 0.f}); sf::Listener::setPosition({m_listener.getPosition().x, m_listener.getPosition().y, 0.f});
m_music->play(); m_music.play();
} }
void onStop() override void onStop() override
{ {
m_music->stop(); m_music.stop();
} }
private: private:
@ -351,7 +351,7 @@ private:
sf::ConvexShape m_soundConeInner; sf::ConvexShape m_soundConeInner;
sf::Text m_text; sf::Text m_text;
sf::Vector2f m_position; sf::Vector2f m_position;
std::optional<sf::Music> m_music; sf::Music m_music;
float m_attenuation{0.01f}; float m_attenuation{0.01f};
}; };
@ -636,7 +636,7 @@ public:
void onUpdate([[maybe_unused]] float time, float x, float y) override void onUpdate([[maybe_unused]] float time, float x, float y) override
{ {
m_position = {windowWidth * x - 10.f, windowHeight * y - 10.f}; m_position = {windowWidth * x - 10.f, windowHeight * y - 10.f};
m_music->setPosition({m_position.x, m_position.y, 0.f}); m_music.setPosition({m_position.x, m_position.y, 0.f});
} }
void onDraw(sf::RenderTarget& target, sf::RenderStates states) const override void onDraw(sf::RenderTarget& target, sf::RenderStates states) const override
@ -656,12 +656,12 @@ public:
// Synchronize listener audio position with graphical position // Synchronize listener audio position with graphical position
sf::Listener::setPosition({m_listener.getPosition().x, m_listener.getPosition().y, 0.f}); sf::Listener::setPosition({m_listener.getPosition().x, m_listener.getPosition().y, 0.f});
m_music->play(); m_music.play();
} }
void onStop() override void onStop() override
{ {
m_music->stop(); m_music.stop();
} }
protected: protected:
@ -677,22 +677,22 @@ protected:
m_instructions.setPosition({windowWidth / 2.f - 250.f, windowHeight * 3.f / 4.f}); m_instructions.setPosition({windowWidth / 2.f - 250.f, windowHeight * 3.f / 4.f});
// Load the music file // Load the music file
if (!(m_music = sf::Music::createFromFile(resourcesDir() / "doodle_pop.ogg"))) if (!m_music.openFromFile(resourcesDir() / "doodle_pop.ogg"))
{ {
std::cerr << "Failed to load " << (resourcesDir() / "doodle_pop.ogg").string() << std::endl; std::cerr << "Failed to load " << (resourcesDir() / "doodle_pop.ogg").string() << std::endl;
std::abort(); std::abort();
} }
// Set the music to loop // Set the music to loop
m_music->setLoop(true); m_music.setLoop(true);
// Set attenuation to a nice value // Set attenuation to a nice value
m_music->setAttenuation(0.0f); m_music.setAttenuation(0.0f);
} }
sf::Music& getMusic() sf::Music& getMusic()
{ {
return *m_music; return m_music;
} }
const std::shared_ptr<bool>& getEnabled() const const std::shared_ptr<bool>& getEnabled() const
@ -712,7 +712,7 @@ private:
sf::CircleShape m_listener{20.f}; sf::CircleShape m_listener{20.f};
sf::CircleShape m_soundShape{20.f}; sf::CircleShape m_soundShape{20.f};
sf::Vector2f m_position; sf::Vector2f m_position;
std::optional<sf::Music> m_music; sf::Music m_music;
std::shared_ptr<bool> m_enabled{std::make_shared<bool>(true)}; std::shared_ptr<bool> m_enabled{std::make_shared<bool>(true)};
sf::Text m_enabledText; sf::Text m_enabledText;
sf::Text m_instructions; sf::Text m_instructions;
@ -1077,7 +1077,7 @@ int main()
window.setVerticalSyncEnabled(true); window.setVerticalSyncEnabled(true);
// Open the application font and pass it to the Effect class // Open the application font and pass it to the Effect class
const auto font = sf::Font::createFromFile(resourcesDir() / "tuffy.ttf").value(); const sf::Font font(resourcesDir() / "tuffy.ttf");
Effect::setFont(font); Effect::setFont(font);
// Create the effects // Create the effects
@ -1106,7 +1106,7 @@ int main()
effects[current]->start(); effects[current]->start();
// Create the messages background // Create the messages background
const auto textBackgroundTexture = sf::Texture::createFromFile(resourcesDir() / "text-background.png").value(); const sf::Texture textBackgroundTexture(resourcesDir() / "text-background.png");
sf::Sprite textBackground(textBackgroundTexture); sf::Sprite textBackground(textBackgroundTexture);
textBackground.setPosition({0.f, 520.f}); textBackground.setPosition({0.f, 520.f});
textBackground.setColor(sf::Color(255, 255, 255, 200)); textBackground.setColor(sf::Color(255, 255, 255, 200));

View File

@ -49,11 +49,11 @@ int main()
window.setVerticalSyncEnabled(true); window.setVerticalSyncEnabled(true);
// Load the sounds used in the game // Load the sounds used in the game
const auto ballSoundBuffer = sf::SoundBuffer::createFromFile(resourcesDir() / "ball.wav").value(); const sf::SoundBuffer ballSoundBuffer(resourcesDir() / "ball.wav");
sf::Sound ballSound(ballSoundBuffer); sf::Sound ballSound(ballSoundBuffer);
// Create the SFML logo texture: // Create the SFML logo texture:
const auto sfmlLogoTexture = sf::Texture::createFromFile(resourcesDir() / "sfml_logo.png").value(); const sf::Texture sfmlLogoTexture(resourcesDir() / "sfml_logo.png");
sf::Sprite sfmlLogo(sfmlLogoTexture); sf::Sprite sfmlLogo(sfmlLogoTexture);
sfmlLogo.setPosition({170.f, 50.f}); sfmlLogo.setPosition({170.f, 50.f});
@ -82,7 +82,7 @@ int main()
ball.setOrigin({ballRadius / 2.f, ballRadius / 2.f}); ball.setOrigin({ballRadius / 2.f, ballRadius / 2.f});
// Open the text font // Open the text font
const auto font = sf::Font::createFromFile(resourcesDir() / "tuffy.ttf").value(); const sf::Font font(resourcesDir() / "tuffy.ttf");
// Initialize the pause message // Initialize the pause message
sf::Text pauseMessage(font); sf::Text pauseMessage(font);

View File

@ -923,17 +923,17 @@ public:
// Use the vertex shader SPIR-V code to create a vertex shader module // Use the vertex shader SPIR-V code to create a vertex shader module
{ {
auto file = sf::FileInputStream::create("resources/shader.vert.spv"); sf::FileInputStream file;
if (!file) if (!file.open("resources/shader.vert.spv"))
{ {
vulkanAvailable = false; vulkanAvailable = false;
return; return;
} }
const auto fileSize = file->getSize().value(); const auto fileSize = file.getSize().value();
std::vector<std::uint32_t> buffer(fileSize / sizeof(std::uint32_t)); std::vector<std::uint32_t> buffer(fileSize / sizeof(std::uint32_t));
if (file->read(buffer.data(), fileSize) != file->getSize()) if (file.read(buffer.data(), fileSize) != file.getSize())
{ {
vulkanAvailable = false; vulkanAvailable = false;
return; return;
@ -951,17 +951,17 @@ public:
// Use the fragment shader SPIR-V code to create a fragment shader module // Use the fragment shader SPIR-V code to create a fragment shader module
{ {
auto file = sf::FileInputStream::create("resources/shader.frag.spv"); sf::FileInputStream file;
if (!file) if (!file.open("resources/shader.frag.spv"))
{ {
vulkanAvailable = false; vulkanAvailable = false;
return; return;
} }
const auto fileSize = file->getSize().value(); const auto fileSize = file.getSize().value();
std::vector<std::uint32_t> buffer(fileSize / sizeof(std::uint32_t)); std::vector<std::uint32_t> buffer(fileSize / sizeof(std::uint32_t));
if (file->read(buffer.data(), fileSize) != file->getSize()) if (file.read(buffer.data(), fileSize) != file.getSize())
{ {
vulkanAvailable = false; vulkanAvailable = false;
return; return;
@ -1801,16 +1801,13 @@ public:
void setupTextureImage() void setupTextureImage()
{ {
// Load the image data // Load the image data
const auto maybeImageData = sf::Image::createFromFile("resources/logo.png"); sf::Image imageData;
if (!imageData.loadFromFile("resources/logo.png"))
if (!maybeImageData)
{ {
vulkanAvailable = false; vulkanAvailable = false;
return; return;
} }
const auto& imageData = *maybeImageData;
// Create a staging buffer to transfer the data with // Create a staging buffer to transfer the data with
const VkDeviceSize imageSize = imageData.getSize().x * imageData.getSize().y * 4; const VkDeviceSize imageSize = imageData.getSize().x * imageData.getSize().y * 4;

View File

@ -111,8 +111,8 @@ int main()
sf::RenderWindow sfmlView2(view2); sf::RenderWindow sfmlView2(view2);
// Load some textures to display // Load some textures to display
const auto texture1 = sf::Texture::createFromFile("resources/image1.jpg").value(); const sf::Texture texture1("resources/image1.jpg");
const auto texture2 = sf::Texture::createFromFile("resources/image2.jpg").value(); const sf::Texture texture2("resources/image2.jpg");
sf::Sprite sprite1(texture1); sf::Sprite sprite1(texture1);
sf::Sprite sprite2(texture2); sf::Sprite sprite2(texture2);
sprite1.setOrigin(sf::Vector2f(texture1.getSize()) / 2.f); sprite1.setOrigin(sf::Vector2f(texture1.getSize()) / 2.f);

View File

@ -33,7 +33,6 @@
#include <filesystem> #include <filesystem>
#include <memory> #include <memory>
#include <optional>
#include <vector> #include <vector>
#include <cstddef> #include <cstddef>
@ -61,6 +60,51 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
InputSoundFile() = default; InputSoundFile() = default;
////////////////////////////////////////////////////////////
/// \brief Construct a sound file from the disk for reading
///
/// The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC, MP3.
/// The supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit.
///
/// Because of minimp3_ex limitation, for MP3 files with big (>16kb) APEv2 tag,
/// it may not be properly removed, tag data will be treated as MP3 data
/// and there is a low chance of garbage decoded at the end of file.
/// See also: https://github.com/lieff/minimp3
///
/// \param filename Path of the sound file to load
///
/// \throws std::runtime_error if opening the file was unsuccessful
///
////////////////////////////////////////////////////////////
InputSoundFile(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Construct a sound file in memory for reading
///
/// The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC.
/// The supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit.
///
/// \param data Pointer to the file data in memory
/// \param sizeInBytes Size of the data to load, in bytes
///
/// \throws std::runtime_error if opening the file was unsuccessful
///
////////////////////////////////////////////////////////////
InputSoundFile(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Construct a sound file from a custom stream for reading
///
/// The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC.
/// The supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit.
///
/// \param stream Source stream to read from
///
/// \throws std::runtime_error if opening the file was unsuccessful
///
////////////////////////////////////////////////////////////
InputSoundFile(InputStream& stream);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Open a sound file from the disk for reading /// \brief Open a sound file from the disk for reading
/// ///
@ -106,51 +150,6 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool openFromStream(InputStream& stream); [[nodiscard]] bool openFromStream(InputStream& stream);
////////////////////////////////////////////////////////////
/// \brief Create a sound file from the disk for reading
///
/// The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC, MP3.
/// The supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit.
///
/// Because of minimp3_ex limitation, for MP3 files with big (>16kb) APEv2 tag,
/// it may not be properly removed, tag data will be treated as MP3 data
/// and there is a low chance of garbage decoded at the end of file.
/// See also: https://github.com/lieff/minimp3
///
/// \param filename Path of the sound file to load
///
/// \return Input sound file if the file was successfully opened, otherwise `std::nullopt`
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<InputSoundFile> createFromFile(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Create a sound file in memory for reading
///
/// The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC.
/// The supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit.
///
/// \param data Pointer to the file data in memory
/// \param sizeInBytes Size of the data to load, in bytes
///
/// \return Input sound file if the file was successfully opened, otherwise `std::nullopt`
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<InputSoundFile> createFromMemory(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Create a sound file from a custom stream for reading
///
/// The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC.
/// The supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit.
///
/// \param stream Source stream to read from
///
/// \return Input sound file if the file was successfully opened, otherwise `std::nullopt`
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<InputSoundFile> createFromStream(InputStream& stream);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Get the total number of audio samples in the file /// \brief Get the total number of audio samples in the file
/// ///
@ -311,7 +310,7 @@ private:
/// Usage example: /// Usage example:
/// \code /// \code
/// // Open a sound file /// // Open a sound file
/// auto file = sf::InputSoundFile::createFromFile("music.ogg").value(); /// sf::InputSoundFile file("music.ogg");
/// ///
/// // Print the sound attributes /// // Print the sound attributes
/// std::cout << "duration: " << file.getDuration().asSeconds() << '\n' /// std::cout << "duration: " << file.getDuration().asSeconds() << '\n'

View File

@ -74,6 +74,71 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Music(); Music();
////////////////////////////////////////////////////////////
/// \brief Construct a music from an audio file
///
/// This function doesn't start playing the music (call play()
/// to do so).
/// See the documentation of sf::InputSoundFile for the list
/// of supported formats.
///
/// \warning Since the music is not loaded at once but rather
/// streamed continuously, the file must remain accessible until
/// the sf::Music object loads a new music or is destroyed.
///
/// \param filename Path of the music file to open
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see openFromMemory, openFromStream
///
////////////////////////////////////////////////////////////
Music(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Construct a music from an audio file in memory
///
/// This function doesn't start playing the music (call play()
/// to do so).
/// See the documentation of sf::InputSoundFile for the list
/// of supported formats.
///
/// \warning Since the music is not loaded at once but rather streamed
/// continuously, the \a data buffer must remain accessible until
/// the sf::Music object loads a new music or is destroyed. That is,
/// you can't deallocate the buffer right after calling this function.
///
/// \param data Pointer to the file data in memory
/// \param sizeInBytes Size of the data to load, in bytes
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see openFromFile, openFromStream
///
////////////////////////////////////////////////////////////
Music(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Construct a music from an audio file in a custom stream
///
/// This function doesn't start playing the music (call play()
/// to do so).
/// See the documentation of sf::InputSoundFile for the list
/// of supported formats.
///
/// \warning Since the music is not loaded at once but rather
/// streamed continuously, the \a stream must remain accessible
/// until the sf::Music object loads a new music or is destroyed.
///
/// \param stream Source stream to read from
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see openFromFile, openFromMemory
///
////////////////////////////////////////////////////////////
Music(InputStream& stream);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Destructor /// \brief Destructor
/// ///
@ -157,71 +222,6 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool openFromStream(InputStream& stream); [[nodiscard]] bool openFromStream(InputStream& stream);
////////////////////////////////////////////////////////////
/// \brief Create a music from an audio file
///
/// This function doesn't start playing the music (call play()
/// to do so).
/// See the documentation of sf::InputSoundFile for the list
/// of supported formats.
///
/// \warning Since the music is not loaded at once but rather
/// streamed continuously, the file must remain accessible until
/// the sf::Music object loads a new music or is destroyed.
///
/// \param filename Path of the music file to open
///
/// \return Music if loading succeeded, `std::nullopt` if it failed
///
/// \see createFromMemory, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Music> createFromFile(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Create a music from an audio file in memory
///
/// This function doesn't start playing the music (call play()
/// to do so).
/// See the documentation of sf::InputSoundFile for the list
/// of supported formats.
///
/// \warning Since the music is not loaded at once but rather streamed
/// continuously, the \a data buffer must remain accessible until
/// the sf::Music object loads a new music or is destroyed. That is,
/// you can't deallocate the buffer right after calling this function.
///
/// \param data Pointer to the file data in memory
/// \param sizeInBytes Size of the data to load, in bytes
///
/// \return Music if loading succeeded, `std::nullopt` if it failed
///
/// \see createFromFile, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Music> createFromMemory(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Create a music from an audio file in a custom stream
///
/// This function doesn't start playing the music (call play()
/// to do so).
/// See the documentation of sf::InputSoundFile for the list
/// of supported formats.
///
/// \warning Since the music is not loaded at once but rather
/// streamed continuously, the \a stream must remain accessible
/// until the sf::Music object loads a new music or is destroyed.
///
/// \param stream Source stream to read from
///
/// \return Music if loading succeeded, `std::nullopt` if it failed
///
/// \see createFromFile, createFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Music> createFromStream(InputStream& stream);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Get the total duration of the music /// \brief Get the total duration of the music
/// ///
@ -360,7 +360,7 @@ private:
/// Usage example: /// Usage example:
/// \code /// \code
/// // Open a music from an audio file /// // Open a music from an audio file
/// auto music = sf::Music::createFromFile("music.ogg").value(); /// sf::Music music("music.ogg");
/// ///
/// // Change some parameters /// // Change some parameters
/// music.setPosition({0, 1, 10}); // change its 3D position /// music.setPosition({0, 1, 10}); // change its 3D position

View File

@ -34,7 +34,6 @@
#include <filesystem> #include <filesystem>
#include <memory> #include <memory>
#include <optional>
#include <vector> #include <vector>
#include <cstdint> #include <cstdint>
@ -58,6 +57,24 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
OutputSoundFile() = default; OutputSoundFile() = default;
////////////////////////////////////////////////////////////
/// \brief Construct the sound file from the disk for writing
///
/// The supported audio formats are: WAV, OGG/Vorbis, FLAC.
///
/// \param filename Path of the sound file to write
/// \param sampleRate Sample rate of the sound
/// \param channelCount Number of channels in the sound
/// \param channelMap Map of position in sample frame to sound channel
///
/// \throws std::runtime_error if the file could not be opened successfully
///
////////////////////////////////////////////////////////////
OutputSoundFile(const std::filesystem::path& filename,
unsigned int sampleRate,
unsigned int channelCount,
const std::vector<SoundChannel>& channelMap);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Open the sound file from the disk for writing /// \brief Open the sound file from the disk for writing
/// ///
@ -76,25 +93,6 @@ public:
unsigned int channelCount, unsigned int channelCount,
const std::vector<SoundChannel>& channelMap); const std::vector<SoundChannel>& channelMap);
////////////////////////////////////////////////////////////
/// \brief Create the sound file from the disk for writing
///
/// The supported audio formats are: WAV, OGG/Vorbis, FLAC.
///
/// \param filename Path of the sound file to write
/// \param sampleRate Sample rate of the sound
/// \param channelCount Number of channels in the sound
/// \param channelMap Map of position in sample frame to sound channel
///
/// \return Output sound file if the file was successfully opened
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<OutputSoundFile> createFromFile(
const std::filesystem::path& filename,
unsigned int sampleRate,
unsigned int channelCount,
const std::vector<SoundChannel>& channelMap);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Write audio samples to the file /// \brief Write audio samples to the file
/// ///
@ -132,7 +130,7 @@ private:
/// Usage example: /// Usage example:
/// \code /// \code
/// // Create a sound file, ogg/vorbis format, 44100 Hz, stereo /// // Create a sound file, ogg/vorbis format, 44100 Hz, stereo
/// auto file = sf::OutputSoundFile::createFromFile("music.ogg", 44100, 2).value(); /// sf::OutputSoundFile file("music.ogg", 44100, 2, {sf::SoundChannel::FrontLeft, sf::SoundChannel::FrontRight});
/// ///
/// while (...) /// while (...)
/// { /// {

View File

@ -274,7 +274,7 @@ private:
/// ///
/// Usage example: /// Usage example:
/// \code /// \code
/// const auto buffer = sf::SoundBuffer::createFromFile("sound.wav").value(); /// const sf::SoundBuffer buffer("sound.wav");
/// sf::Sound sound(buffer); /// sf::Sound sound(buffer);
/// sound.play(); /// sound.play();
/// \endcode /// \endcode

View File

@ -34,7 +34,6 @@
#include <SFML/System/Time.hpp> #include <SFML/System/Time.hpp>
#include <filesystem> #include <filesystem>
#include <optional>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
@ -72,6 +71,74 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
SoundBuffer(const SoundBuffer& copy); SoundBuffer(const SoundBuffer& copy);
////////////////////////////////////////////////////////////
/// \brief Construct the sound buffer from a file
///
/// See the documentation of sf::InputSoundFile for the list
/// of supported formats.
///
/// \param filename Path of the sound file to load
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromMemory, loadFromStream, loadFromSamples, saveToFile
///
////////////////////////////////////////////////////////////
SoundBuffer(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Construct the sound buffer from a file in memory
///
/// See the documentation of sf::InputSoundFile for the list
/// of supported formats.
///
/// \param data Pointer to the file data in memory
/// \param sizeInBytes Size of the data to load, in bytes
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromStream, loadFromSamples
///
////////////////////////////////////////////////////////////
SoundBuffer(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Construct the sound buffer from a custom stream
///
/// See the documentation of sf::InputSoundFile for the list
/// of supported formats.
///
/// \param stream Source stream to read from
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromSamples
///
////////////////////////////////////////////////////////////
SoundBuffer(InputStream& stream);
////////////////////////////////////////////////////////////
/// \brief Construct the sound buffer from an array of audio samples
///
/// The assumed format of the audio samples is 16 bit signed integer.
///
/// \param samples Pointer to the array of samples in memory
/// \param sampleCount Number of samples in the array
/// \param channelCount Number of channels (1 = mono, 2 = stereo, ...)
/// \param sampleRate Sample rate (number of samples to play per second)
/// \param channelMap Map of position in sample frame to sound channel
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, saveToFile
///
////////////////////////////////////////////////////////////
SoundBuffer(const std::int16_t* samples,
std::uint64_t sampleCount,
unsigned int channelCount,
unsigned int sampleRate,
const std::vector<SoundChannel>& channelMap);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Destructor /// \brief Destructor
/// ///
@ -146,75 +213,6 @@ public:
unsigned int sampleRate, unsigned int sampleRate,
const std::vector<SoundChannel>& channelMap); const std::vector<SoundChannel>& channelMap);
////////////////////////////////////////////////////////////
/// \brief Create the sound buffer from a file
///
/// See the documentation of sf::InputSoundFile for the list
/// of supported formats.
///
/// \param filename Path of the sound file to load
///
/// \return Sound buffer if loading succeeded, `std::nullopt` if it failed
///
/// \see createFromMemory, createFromStream, createFromSamples, saveToFile
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<SoundBuffer> createFromFile(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Create the sound buffer from a file in memory
///
/// See the documentation of sf::InputSoundFile for the list
/// of supported formats.
///
/// \param data Pointer to the file data in memory
/// \param sizeInBytes Size of the data to load, in bytes
///
/// \return Sound buffer if loading succeeded, `std::nullopt` if it failed
///
/// \see createFromFile, createFromStream, createFromSamples
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<SoundBuffer> createFromMemory(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Create the sound buffer from a custom stream
///
/// See the documentation of sf::InputSoundFile for the list
/// of supported formats.
///
/// \param stream Source stream to read from
///
/// \return Sound buffer if loading succeeded, `std::nullopt` if it failed
///
/// \see createFromFile, createFromMemory, createFromSamples
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<SoundBuffer> createFromStream(InputStream& stream);
////////////////////////////////////////////////////////////
/// \brief Create the sound buffer from an array of audio samples
///
/// The assumed format of the audio samples is 16 bits signed integer.
///
/// \param samples Pointer to the array of samples in memory
/// \param sampleCount Number of samples in the array
/// \param channelCount Number of channels (1 = mono, 2 = stereo, ...)
/// \param sampleRate Sample rate (number of samples to play per second)
/// \param channelMap Map of position in sample frame to sound channel
///
/// \return Sound buffer if loading succeeded, `std::nullopt` if it failed
///
/// \see createFromFile, createFromMemory, saveToFile
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<SoundBuffer> createFromSamples(
const std::int16_t* samples,
std::uint64_t sampleCount,
unsigned int channelCount,
unsigned int sampleRate,
const std::vector<SoundChannel>& channelMap);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Save the sound buffer to an audio file /// \brief Save the sound buffer to an audio file
/// ///
@ -225,8 +223,6 @@ public:
/// ///
/// \return True if saving succeeded, false if it failed /// \return True if saving succeeded, false if it failed
/// ///
/// \see createFromFile, createFromMemory, createFromSamples
///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool saveToFile(const std::filesystem::path& filename) const; [[nodiscard]] bool saveToFile(const std::filesystem::path& filename) const;
@ -389,8 +385,7 @@ private:
/// are like texture pixels, and a sf::SoundBuffer is similar to /// are like texture pixels, and a sf::SoundBuffer is similar to
/// a sf::Texture. /// a sf::Texture.
/// ///
/// A sound buffer can be loaded from a file (see createFromFile() /// A sound buffer can be loaded from a file, from memory, from
/// for the complete list of supported formats), from memory, from
/// a custom stream (see sf::InputStream) or directly from an array /// a custom stream (see sf::InputStream) or directly from an array
/// of samples. It can also be saved back to a file. /// of samples. It can also be saved back to a file.
/// ///
@ -415,7 +410,7 @@ private:
/// Usage example: /// Usage example:
/// \code /// \code
/// // Load a new sound buffer from a file /// // Load a new sound buffer from a file
/// const auto buffer = sf::SoundBuffer::createFromFile("sound.wav").value(); /// const sf::SoundBuffer buffer("sound.wav");
/// ///
/// // Create a sound source bound to the buffer /// // Create a sound source bound to the buffer
/// sf::Sound sound1(buffer); /// sf::Sound sound1(buffer);

View File

@ -37,7 +37,6 @@
#include <filesystem> #include <filesystem>
#include <memory> #include <memory>
#include <optional>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
@ -81,6 +80,71 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Font() = default; Font() = default;
////////////////////////////////////////////////////////////
/// \brief Construct the font from a file
///
/// The supported font formats are: TrueType, Type 1, CFF,
/// OpenType, SFNT, X11 PCF, Windows FNT, BDF, PFR and Type 42.
/// Note that this function knows nothing about the standard
/// fonts installed on the user's system, thus you can't
/// load them directly.
///
/// \warning SFML cannot preload all the font data in this
/// function, so the file has to remain accessible until
/// the sf::Font object opens a new font or is destroyed.
///
/// \param filename Path of the font file to open
///
/// \throws std::runtime_error if opening was unsuccessful
///
/// \see openFromFile, openFromMemory, openFromStream
///
////////////////////////////////////////////////////////////
Font(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Construct the font from a file in memory
///
/// The supported font formats are: TrueType, Type 1, CFF,
/// OpenType, SFNT, X11 PCF, Windows FNT, BDF, PFR and Type 42.
///
/// \warning SFML cannot preload all the font data in this
/// function, so the buffer pointed by \a data has to remain
/// valid until the sf::Font object opens a new font or
/// is destroyed.
///
/// \param data Pointer to the file data in memory
/// \param sizeInBytes Size of the data to load, in bytes
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see openFromFile, openFromMemory, openFromStream
///
////////////////////////////////////////////////////////////
Font(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Construct the font from a custom stream
///
/// The supported font formats are: TrueType, Type 1, CFF,
/// OpenType, SFNT, X11 PCF, Windows FNT, BDF, PFR and Type 42.
/// Warning: SFML cannot preload all the font data in this
/// function, so the contents of \a stream have to remain
/// valid as long as the font is used.
///
/// \warning SFML cannot preload all the font data in this
/// function, so the stream has to remain accessible until
/// the sf::Font object opens a new font or is destroyed.
///
/// \param stream Source stream to read from
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see openFromFile, openFromMemory, openFromStream
///
////////////////////////////////////////////////////////////
Font(InputStream& stream);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Open the font from a file /// \brief Open the font from a file
/// ///
@ -92,11 +156,11 @@ public:
/// ///
/// \warning SFML cannot preload all the font data in this /// \warning SFML cannot preload all the font data in this
/// function, so the file has to remain accessible until /// function, so the file has to remain accessible until
/// the sf::Font object loads a new font or is destroyed. /// the sf::Font object opens a new font or is destroyed.
/// ///
/// \param filename Path of the font file to load /// \param filename Path of the font file to load
/// ///
/// \return True if loading succeeded, false if it failed /// \return True if opening succeeded, false if it failed
/// ///
/// \see openFromMemory, openFromStream /// \see openFromMemory, openFromStream
/// ///
@ -111,13 +175,13 @@ public:
/// ///
/// \warning SFML cannot preload all the font data in this /// \warning SFML cannot preload all the font data in this
/// function, so the buffer pointed by \a data has to remain /// function, so the buffer pointed by \a data has to remain
/// valid until the sf::Font object loads a new font or /// valid until the sf::Font object opens a new font or
/// is destroyed. /// is destroyed.
/// ///
/// \param data Pointer to the file data in memory /// \param data Pointer to the file data in memory
/// \param sizeInBytes Size of the data to load, in bytes /// \param sizeInBytes Size of the data to load, in bytes
/// ///
/// \return True if loading succeeded, false if it failed /// \return True if opening succeeded, false if it failed
/// ///
/// \see openFromFile, openFromStream /// \see openFromFile, openFromStream
/// ///
@ -129,84 +193,20 @@ public:
/// ///
/// The supported font formats are: TrueType, Type 1, CFF, /// The supported font formats are: TrueType, Type 1, CFF,
/// OpenType, SFNT, X11 PCF, Windows FNT, BDF, PFR and Type 42. /// OpenType, SFNT, X11 PCF, Windows FNT, BDF, PFR and Type 42.
/// Warning: SFML cannot preload all the font data in this
/// function, so the contents of \a stream have to remain
/// valid as long as the font is used.
/// ///
/// \warning SFML cannot preload all the font data in this /// \warning SFML cannot preload all the font data in this
/// function, so the stream has to remain accessible until /// function, so the stream has to remain accessible until
/// the sf::Font object loads a new font or is destroyed. /// the sf::Font object opens a new font or is destroyed.
/// ///
/// \param stream Source stream to read from /// \param stream Source stream to read from
/// ///
/// \return True if loading succeeded, false if it failed /// \return True if opening succeeded, false if it failed
/// ///
/// \see openFromFile, openFromMemory /// \see openFromFile, openFromMemory
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool openFromStream(InputStream& stream); [[nodiscard]] bool openFromStream(InputStream& stream);
////////////////////////////////////////////////////////////
/// \brief Create the font from a file
///
/// The supported font formats are: TrueType, Type 1, CFF,
/// OpenType, SFNT, X11 PCF, Windows FNT, BDF, PFR and Type 42.
/// Note that this function knows nothing about the standard
/// fonts installed on the user's system, thus you can't
/// load them directly.
///
/// \warning SFML cannot preload all the font data in this
/// function, so the file has to remain accessible until
/// the sf::Font object is destroyed.
///
/// \param filename Path of the font file to load
///
/// \return Font if opening succeeded, `std::nullopt` if it failed
///
/// \see createFromMemory, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Font> createFromFile(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Create the font from a file in memory
///
/// The supported font formats are: TrueType, Type 1, CFF,
/// OpenType, SFNT, X11 PCF, Windows FNT, BDF, PFR and Type 42.
///
/// \warning SFML cannot preload all the font data in this
/// function, so the buffer pointed by \a data has to remain
/// valid until the sf::Font object is destroyed.
///
/// \param data Pointer to the file data in memory
/// \param sizeInBytes Size of the data to load, in bytes
///
/// \return Font if opening succeeded, `std::nullopt` if it failed
///
/// \see createFromFile, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Font> createFromMemory(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Create the font from a custom stream
///
/// The supported font formats are: TrueType, Type 1, CFF,
/// OpenType, SFNT, X11 PCF, Windows FNT, BDF, PFR and Type 42.
///
/// \warning SFML cannot preload all the font data in this
/// function, so the stream has to remain accessible until
/// the sf::Font object is destroyed.
///
/// \param stream Source stream to read from
///
/// \return Font if opening succeeded, `std::nullopt` if it failed
///
/// \see createFromFile, createFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Font> createFromStream(InputStream& stream);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Get the font information /// \brief Get the font information
/// ///
@ -474,7 +474,7 @@ private:
/// ///
/// Fonts can be opened from a file, from memory or from a custom /// Fonts can be opened from a file, from memory or from a custom
/// stream, and supports the most common types of fonts. See /// stream, and supports the most common types of fonts. See
/// the createFromFile function for the complete list of supported formats. /// the openFromFile function for the complete list of supported formats.
/// ///
/// Once it is opened, a sf::Font instance provides three /// Once it is opened, a sf::Font instance provides three
/// types of information about the font: /// types of information about the font:
@ -505,7 +505,7 @@ private:
/// Usage example: /// Usage example:
/// \code /// \code
/// // Open a new font /// // Open a new font
/// const auto font = sf::Font::createFromFile("arial.ttf").value(); /// const sf::Font font("arial.ttf");
/// ///
/// // Create a text which uses our font /// // Create a text which uses our font
/// sf::Text text1(font); /// sf::Text text1(font);

View File

@ -59,6 +59,8 @@ public:
/// ///
/// Constructs an image with width 0 and height 0. /// Constructs an image with width 0 and height 0.
/// ///
/// \see resize
///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Image() = default; Image() = default;
@ -85,6 +87,55 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Image(Vector2u size, const std::uint8_t* pixels); Image(Vector2u size, const std::uint8_t* pixels);
////////////////////////////////////////////////////////////
/// \brief Construct the image from a file on disk
///
/// The supported image formats are bmp, png, tga, jpg, gif,
/// psd, hdr, pic and pnm. Some format options are not supported,
/// like jpeg with arithmetic coding or ASCII pnm.
///
/// \param filename Path of the image file to load
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream
///
////////////////////////////////////////////////////////////
explicit Image(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Construct the image from a file in memory
///
/// The supported image formats are bmp, png, tga, jpg, gif,
/// psd, hdr, pic and pnm. Some format options are not supported,
/// like jpeg with arithmetic coding or ASCII pnm.
///
/// \param data Pointer to the file data in memory
/// \param size Size of the data to load, in bytes
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream
///
////////////////////////////////////////////////////////////
Image(const void* data, std::size_t size);
////////////////////////////////////////////////////////////
/// \brief Construct the image from a custom stream
///
/// The supported image formats are bmp, png, tga, jpg, gif,
/// psd, hdr, pic and pnm. Some format options are not supported,
/// like jpeg with arithmetic coding or ASCII pnm.
///
/// \param stream Source stream to read from
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream
///
////////////////////////////////////////////////////////////
explicit Image(InputStream& stream);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Resize the image and fill it with a unique color /// \brief Resize the image and fill it with a unique color
/// ///
@ -138,7 +189,7 @@ public:
/// ///
/// \return True if loading was successful /// \return True if loading was successful
/// ///
/// \see loadFromFile, loadFromStream /// \see loadFromFile, loadFromStream, saveToMemory
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromMemory(const void* data, std::size_t size); [[nodiscard]] bool loadFromMemory(const void* data, std::size_t size);
@ -160,58 +211,6 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromStream(InputStream& stream); [[nodiscard]] bool loadFromStream(InputStream& stream);
////////////////////////////////////////////////////////////
/// \brief Load the image from a file on disk
///
/// The supported image formats are bmp, png, tga, jpg, gif,
/// psd, hdr, pic and pnm. Some format options are not supported,
/// like jpeg with arithmetic coding or ASCII pnm.
/// If this function fails, the image is left unchanged.
///
/// \param filename Path of the image file to load
///
/// \return Image if loading was successful, `std::nullopt` otherwise
///
/// \see createFromMemory, createFromStream, saveToFile
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Image> createFromFile(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Load the image from a file in memory
///
/// The supported image formats are bmp, png, tga, jpg, gif,
/// psd, hdr, pic and pnm. Some format options are not supported,
/// like jpeg with arithmetic coding or ASCII pnm.
/// If this function fails, the image is left unchanged.
///
/// \param data Pointer to the file data in memory
/// \param size Size of the data to load, in bytes
///
/// \return Image if loading was successful, `std::nullopt` otherwise
///
/// \see createFromFile, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Image> createFromMemory(const void* data, std::size_t size);
////////////////////////////////////////////////////////////
/// \brief Load the image from a custom stream
///
/// The supported image formats are bmp, png, tga, jpg, gif,
/// psd, hdr, pic and pnm. Some format options are not supported,
/// like jpeg with arithmetic coding or ASCII pnm.
/// If this function fails, the image is left unchanged.
///
/// \param stream Source stream to read from
///
/// \return Image if loading was successful, `std::nullopt` otherwise
///
/// \see createFromFile, createFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Image> createFromStream(InputStream& stream);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Save the image to a file on disk /// \brief Save the image to a file on disk
/// ///
@ -224,7 +223,7 @@ public:
/// ///
/// \return True if saving was successful /// \return True if saving was successful
/// ///
/// \see create, createFromFile, createFromMemory /// \see saveToMemory, loadFromFile
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool saveToFile(const std::filesystem::path& filename) const; [[nodiscard]] bool saveToFile(const std::filesystem::path& filename) const;
@ -242,7 +241,7 @@ public:
/// \return Buffer with encoded data if saving was successful, /// \return Buffer with encoded data if saving was successful,
/// otherwise std::nullopt /// otherwise std::nullopt
/// ///
/// \see create, createFromFile, createFromMemory, saveToFile /// \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(std::string_view format) const;
@ -386,7 +385,7 @@ private:
/// channels -- just like a sf::Color. /// channels -- just like a sf::Color.
/// All the functions that return an array of pixels follow /// All the functions that return an array of pixels follow
/// this rule, and all parameters that you pass to sf::Image /// this rule, and all parameters that you pass to sf::Image
/// functions (such as createFromMemory) must use this /// functions (such as loadFromMemory) must use this
/// representation as well. /// representation as well.
/// ///
/// A sf::Image can be copied, but it is a heavy resource and /// A sf::Image can be copied, but it is a heavy resource and
@ -396,7 +395,7 @@ private:
/// Usage example: /// Usage example:
/// \code /// \code
/// // Load an image file from a file /// // Load an image file from a file
/// const auto background = sf::Image::createFromFile("background.jpg").value(); /// const sf::Image background("background.jpg");
/// ///
/// // Create a 20x20 image filled with black color /// // Create a 20x20 image filled with black color
/// sf::Image image({20, 20}, sf::Color::Black); /// sf::Image image({20, 20}, sf::Color::Black);

View File

@ -37,7 +37,6 @@
#include <SFML/System/Vector2.hpp> #include <SFML/System/Vector2.hpp>
#include <memory> #include <memory>
#include <optional>
namespace sf namespace sf
@ -64,6 +63,25 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
RenderTexture(); RenderTexture();
////////////////////////////////////////////////////////////
/// \brief Construct a render-texture
///
/// The last parameter, \a settings, is useful if you want to enable
/// multi-sampling or use the render-texture for OpenGL rendering that
/// requires a depth or stencil buffer. Otherwise it is unnecessary, and
/// you should leave this parameter at its default value.
///
/// After creation, the contents of the render-texture are undefined.
/// Call `RenderTexture::clear` first to ensure a single color fill.
///
/// \param size Width and height of the render-texture
/// \param settings Additional settings for the underlying OpenGL texture and context
///
/// \throws std::runtime_error if creation was unsuccessful
///
////////////////////////////////////////////////////////////
RenderTexture(Vector2u size, const ContextSettings& settings = {});
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Destructor /// \brief Destructor
/// ///
@ -108,29 +126,10 @@ public:
/// \param size Width and height of the render-texture /// \param size Width and height of the render-texture
/// \param settings Additional settings for the underlying OpenGL texture and context /// \param settings Additional settings for the underlying OpenGL texture and context
/// ///
/// \return True if resizing has been successful /// \return True if resizing has been successful, false if it failed
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool resize(Vector2u size, const ContextSettings& settings = ContextSettings()); [[nodiscard]] bool resize(Vector2u size, const ContextSettings& settings = {});
////////////////////////////////////////////////////////////
/// \brief Create the render-texture
///
/// The last parameter, \a settings, is useful if you want to enable
/// multi-sampling or use the render-texture for OpenGL rendering that
/// requires a depth or stencil buffer. Otherwise it is unnecessary, and
/// you should leave this parameter at its default value.
///
/// After creation, the contents of the render-texture are undefined.
/// Call `RenderTexture::clear` first to ensure a single color fill.
///
/// \param size Width and height of the render-texture
/// \param settings Additional settings for the underlying OpenGL texture and context
///
/// \return Render texture if creation has been successful, otherwise `std::nullopt`
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<RenderTexture> create(Vector2u size, const ContextSettings& settings = {});
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Get the maximum anti-aliasing level supported by the system /// \brief Get the maximum anti-aliasing level supported by the system
@ -302,7 +301,7 @@ private:
/// sf::RenderWindow window(sf::VideoMode({800, 600}), "SFML window"); /// sf::RenderWindow window(sf::VideoMode({800, 600}), "SFML window");
/// ///
/// // Create a new render-texture /// // Create a new render-texture
/// auto texture = sf::RenderTexture::create({500, 500}).value(); /// sf::RenderTexture texture({500, 500});
/// ///
/// // The main loop /// // The main loop
/// while (window.isOpen()) /// while (window.isOpen())

View File

@ -87,7 +87,7 @@ public:
const String& title, const String& title,
std::uint32_t style = Style::Default, std::uint32_t style = Style::Default,
State state = State::Windowed, State state = State::Windowed,
const ContextSettings& settings = ContextSettings()); const ContextSettings& settings = {});
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Construct a new window /// \brief Construct a new window
@ -106,7 +106,7 @@ public:
/// \param settings Additional settings for the underlying OpenGL context /// \param settings Additional settings for the underlying OpenGL context
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
RenderWindow(VideoMode mode, const String& title, State state, const ContextSettings& settings = ContextSettings()); RenderWindow(VideoMode mode, const String& title, State state, const ContextSettings& settings = {});
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Construct the window from an existing control /// \brief Construct the window from an existing control
@ -124,7 +124,7 @@ public:
/// \param settings Additional settings for the underlying OpenGL context /// \param settings Additional settings for the underlying OpenGL context
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
explicit RenderWindow(WindowHandle handle, const ContextSettings& settings = ContextSettings()); explicit RenderWindow(WindowHandle handle, const ContextSettings& settings = {});
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Get the size of the rendering region of the window /// \brief Get the size of the rendering region of the window
@ -266,9 +266,9 @@ private:
/// sf::RenderWindow window(sf::VideoMode({800, 600}), "SFML OpenGL"); /// sf::RenderWindow window(sf::VideoMode({800, 600}), "SFML OpenGL");
/// ///
/// // Create a sprite and a text to display /// // Create a sprite and a text to display
/// const auto texture = sf::Texture::createFromFile("circle.png").value(); /// const sf::Texture texture("circle.png");
/// sf::Sprite sprite(texture); /// sf::Sprite sprite(texture);
/// const auto font = sf::Font::createFromFile("arial.ttf").value(); /// const sf::Font font("arial.ttf");
/// sf::Text text(font); /// sf::Text text(font);
/// ... /// ...
/// ///

View File

@ -34,7 +34,6 @@
#include <SFML/Window/GlResource.hpp> #include <SFML/Window/GlResource.hpp>
#include <filesystem> #include <filesystem>
#include <optional>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <unordered_map> #include <unordered_map>
@ -126,6 +125,198 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Shader& operator=(Shader&& right) noexcept; Shader& operator=(Shader&& right) noexcept;
////////////////////////////////////////////////////////////
/// \brief Construct from a shader file
///
/// This constructor loads a single shader, vertex, geometry or
/// fragment, identified by the second argument.
/// The source must be a text file containing a valid
/// shader in GLSL language. GLSL is a C-like language
/// dedicated to OpenGL shaders; you'll probably need to
/// read a good documentation for it before writing your
/// own shaders.
///
/// \param filename Path of the vertex, geometry or fragment shader file to load
/// \param type Type of shader (vertex, geometry or fragment)
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream
///
////////////////////////////////////////////////////////////
Shader(const std::filesystem::path& filename, Type type);
////////////////////////////////////////////////////////////
/// \brief Construct from vertex and fragment shader files
///
/// This constructor loads both the vertex and the fragment
/// shaders. If one of them fails to load, the shader is left
/// empty (the valid shader is unloaded).
/// The sources must be text files containing valid shaders
/// in GLSL language. GLSL is a C-like language dedicated to
/// OpenGL shaders; you'll probably need to read a good documentation
/// for it before writing your own shaders.
///
/// \param vertexShaderFilename Path of the vertex shader file to load
/// \param fragmentShaderFilename Path of the fragment shader file to load
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream
///
////////////////////////////////////////////////////////////
Shader(const std::filesystem::path& vertexShaderFilename, const std::filesystem::path& fragmentShaderFilename);
////////////////////////////////////////////////////////////
/// \brief Construct from vertex, geometry and fragment shader files
///
/// This constructor loads the vertex, geometry and fragment
/// shaders. If one of them fails to load, the shader is left
/// empty (the valid shader is unloaded).
/// The sources must be text files containing valid shaders
/// in GLSL language. GLSL is a C-like language dedicated to
/// OpenGL shaders; you'll probably need to read a good documentation
/// for it before writing your own shaders.
///
/// \param vertexShaderFilename Path of the vertex shader file to load
/// \param geometryShaderFilename Path of the geometry shader file to load
/// \param fragmentShaderFilename Path of the fragment shader file to load
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream
///
////////////////////////////////////////////////////////////
Shader(const std::filesystem::path& vertexShaderFilename,
const std::filesystem::path& geometryShaderFilename,
const std::filesystem::path& fragmentShaderFilename);
////////////////////////////////////////////////////////////
/// \brief Construct from shader in memory
///
/// This constructor loads a single shader, vertex, geometry
/// or fragment, identified by the second argument.
/// The source code must be a valid shader in GLSL language.
/// GLSL is a C-like language dedicated to OpenGL shaders;
/// you'll probably need to read a good documentation for
/// it before writing your own shaders.
///
/// \param shader String containing the source code of the shader
/// \param type Type of shader (vertex, geometry or fragment)
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream
///
////////////////////////////////////////////////////////////
Shader(std::string_view shader, Type type);
////////////////////////////////////////////////////////////
/// \brief Construct from vertex and fragment shaders in memory
///
/// This constructor loads both the vertex and the fragment
/// shaders. If one of them fails to load, the shader is left
/// empty (the valid shader is unloaded).
/// The sources must be valid shaders in GLSL language. GLSL is
/// a C-like language dedicated to OpenGL shaders; you'll
/// probably need to read a good documentation for it before
/// writing your own shaders.
///
/// \param vertexShader String containing the source code of the vertex shader
/// \param fragmentShader String containing the source code of the fragment shader
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream
///
////////////////////////////////////////////////////////////
Shader(std::string_view vertexShader, std::string_view fragmentShader);
////////////////////////////////////////////////////////////
/// \brief Construct from vertex, geometry and fragment shaders in memory
///
/// This constructor loads the vertex, geometry and fragment
/// shaders. If one of them fails to load, the shader is left
/// empty (the valid shader is unloaded).
/// The sources must be valid shaders in GLSL language. GLSL is
/// a C-like language dedicated to OpenGL shaders; you'll
/// probably need to read a good documentation for it before
/// writing your own shaders.
///
/// \param vertexShader String containing the source code of the vertex shader
/// \param geometryShader String containing the source code of the geometry shader
/// \param fragmentShader String containing the source code of the fragment shader
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream
///
////////////////////////////////////////////////////////////
Shader(std::string_view vertexShader, std::string_view geometryShader, std::string_view fragmentShader);
////////////////////////////////////////////////////////////
/// \brief Construct from a shader stream
///
/// This constructor loads a single shader, vertex, geometry
/// or fragment, identified by the second argument.
/// The source code must be a valid shader in GLSL language.
/// GLSL is a C-like language dedicated to OpenGL shaders;
/// you'll probably need to read a good documentation for it
/// before writing your own shaders.
///
/// \param stream Source stream to read from
/// \param type Type of shader (vertex, geometry or fragment)
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream
///
////////////////////////////////////////////////////////////
Shader(InputStream& stream, Type type);
////////////////////////////////////////////////////////////
/// \brief Construct from vertex and fragment shader streams
///
/// This constructor loads both the vertex and the fragment
/// shaders. If one of them fails to load, the shader is left
/// empty (the valid shader is unloaded).
/// The source codes must be valid shaders in GLSL language.
/// GLSL is a C-like language dedicated to OpenGL shaders;
/// you'll probably need to read a good documentation for
/// it before writing your own shaders.
///
/// \param vertexShaderStream Source stream to read the vertex shader from
/// \param fragmentShaderStream Source stream to read the fragment shader from
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream
///
////////////////////////////////////////////////////////////
Shader(InputStream& vertexShaderStream, InputStream& fragmentShaderStream);
////////////////////////////////////////////////////////////
/// \brief Construct from vertex, geometry and fragment shader streams
///
/// This constructor loads the vertex, geometry and fragment
/// shaders. If one of them fails to load, the shader is left
/// empty (the valid shader is unloaded).
/// The source codes must be valid shaders in GLSL language.
/// GLSL is a C-like language dedicated to OpenGL shaders;
/// you'll probably need to read a good documentation for
/// it before writing your own shaders.
///
/// \param vertexShaderStream Source stream to read the vertex shader from
/// \param geometryShaderStream Source stream to read the geometry shader from
/// \param fragmentShaderStream Source stream to read the fragment shader from
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream
///
////////////////////////////////////////////////////////////
Shader(InputStream& vertexShaderStream, InputStream& geometryShaderStream, InputStream& fragmentShaderStream);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Load the vertex, geometry or fragment shader from a file /// \brief Load the vertex, geometry or fragment shader from a file
/// ///
@ -323,204 +514,6 @@ public:
InputStream& geometryShaderStream, InputStream& geometryShaderStream,
InputStream& fragmentShaderStream); InputStream& fragmentShaderStream);
////////////////////////////////////////////////////////////
/// \brief Load the vertex, geometry or fragment shader from a file
///
/// This function loads a single shader, vertex, geometry or
/// fragment, identified by the second argument.
/// The source must be a text file containing a valid
/// shader in GLSL language. GLSL is a C-like language
/// dedicated to OpenGL shaders; you'll probably need to
/// read a good documentation for it before writing your
/// own shaders.
///
/// \param filename Path of the vertex, geometry or fragment shader file to load
/// \param type Type of shader (vertex, geometry or fragment)
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see createFromMemory, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> createFromFile(const std::filesystem::path& filename, Type type);
////////////////////////////////////////////////////////////
/// \brief Load both the vertex and fragment shaders from files
///
/// This function loads both the vertex and the fragment
/// shaders. If one of them fails to load, the shader is left
/// empty (the valid shader is unloaded).
/// The sources must be text files containing valid shaders
/// in GLSL language. GLSL is a C-like language dedicated to
/// OpenGL shaders; you'll probably need to read a good documentation
/// for it before writing your own shaders.
///
/// \param vertexShaderFilename Path of the vertex shader file to load
/// \param fragmentShaderFilename Path of the fragment shader file to load
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see createFromMemory, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> createFromFile(const std::filesystem::path& vertexShaderFilename,
const std::filesystem::path& fragmentShaderFilename);
////////////////////////////////////////////////////////////
/// \brief Load the vertex, geometry and fragment shaders from files
///
/// This function loads the vertex, geometry and fragment
/// shaders. If one of them fails to load, the shader is left
/// empty (the valid shader is unloaded).
/// The sources must be text files containing valid shaders
/// in GLSL language. GLSL is a C-like language dedicated to
/// OpenGL shaders; you'll probably need to read a good documentation
/// for it before writing your own shaders.
///
/// \param vertexShaderFilename Path of the vertex shader file to load
/// \param geometryShaderFilename Path of the geometry shader file to load
/// \param fragmentShaderFilename Path of the fragment shader file to load
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see createFromMemory, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> createFromFile(const std::filesystem::path& vertexShaderFilename,
const std::filesystem::path& geometryShaderFilename,
const std::filesystem::path& fragmentShaderFilename);
////////////////////////////////////////////////////////////
/// \brief Load the vertex, geometry or fragment shader from a source code in memory
///
/// This function loads a single shader, vertex, geometry
/// or fragment, identified by the second argument.
/// The source code must be a valid shader in GLSL language.
/// GLSL is a C-like language dedicated to OpenGL shaders;
/// you'll probably need to read a good documentation for
/// it before writing your own shaders.
///
/// \param shader String containing the source code of the shader
/// \param type Type of shader (vertex, geometry or fragment)
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see createFromFile, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> createFromMemory(std::string_view shader, Type type);
////////////////////////////////////////////////////////////
/// \brief Load both the vertex and fragment shaders from source codes in memory
///
/// This function loads both the vertex and the fragment
/// shaders. If one of them fails to load, the shader is left
/// empty (the valid shader is unloaded).
/// The sources must be valid shaders in GLSL language. GLSL is
/// a C-like language dedicated to OpenGL shaders; you'll
/// probably need to read a good documentation for it before
/// writing your own shaders.
///
/// \param vertexShader String containing the source code of the vertex shader
/// \param fragmentShader String containing the source code of the fragment shader
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see createFromFile, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> createFromMemory(std::string_view vertexShader, std::string_view fragmentShader);
////////////////////////////////////////////////////////////
/// \brief Load the vertex, geometry and fragment shaders from source codes in memory
///
/// This function loads the vertex, geometry and fragment
/// shaders. If one of them fails to load, the shader is left
/// empty (the valid shader is unloaded).
/// The sources must be valid shaders in GLSL language. GLSL is
/// a C-like language dedicated to OpenGL shaders; you'll
/// probably need to read a good documentation for it before
/// writing your own shaders.
///
/// \param vertexShader String containing the source code of the vertex shader
/// \param geometryShader String containing the source code of the geometry shader
/// \param fragmentShader String containing the source code of the fragment shader
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see createFromFile, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> createFromMemory(std::string_view vertexShader,
std::string_view geometryShader,
std::string_view fragmentShader);
////////////////////////////////////////////////////////////
/// \brief Load the vertex, geometry or fragment shader from a custom stream
///
/// This function loads a single shader, vertex, geometry
/// or fragment, identified by the second argument.
/// The source code must be a valid shader in GLSL language.
/// GLSL is a C-like language dedicated to OpenGL shaders;
/// you'll probably need to read a good documentation for it
/// before writing your own shaders.
///
/// \param stream Source stream to read from
/// \param type Type of shader (vertex, geometry or fragment)
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see createFromFile, createFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> createFromStream(InputStream& stream, Type type);
////////////////////////////////////////////////////////////
/// \brief Load both the vertex and fragment shaders from custom streams
///
/// This function loads both the vertex and the fragment
/// shaders. If one of them fails to load, the shader is left
/// empty (the valid shader is unloaded).
/// The source codes must be valid shaders in GLSL language.
/// GLSL is a C-like language dedicated to OpenGL shaders;
/// you'll probably need to read a good documentation for
/// it before writing your own shaders.
///
/// \param vertexShaderStream Source stream to read the vertex shader from
/// \param fragmentShaderStream Source stream to read the fragment shader from
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see createFromFile, createFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> createFromStream(InputStream& vertexShaderStream,
InputStream& fragmentShaderStream);
////////////////////////////////////////////////////////////
/// \brief Load the vertex, geometry and fragment shaders from custom streams
///
/// This function loads the vertex, geometry and fragment
/// shaders. If one of them fails to load, the shader is left
/// empty (the valid shader is unloaded).
/// The source codes must be valid shaders in GLSL language.
/// GLSL is a C-like language dedicated to OpenGL shaders;
/// you'll probably need to read a good documentation for
/// it before writing your own shaders.
///
/// \param vertexShaderStream Source stream to read the vertex shader from
/// \param geometryShaderStream Source stream to read the geometry shader from
/// \param fragmentShaderStream Source stream to read the fragment shader from
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see createFromFile, createFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> createFromStream(InputStream& vertexShaderStream,
InputStream& geometryShaderStream,
InputStream& fragmentShaderStream);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Specify value for \p float uniform /// \brief Specify value for \p float uniform
/// ///

View File

@ -261,7 +261,7 @@ private:
/// Usage example: /// Usage example:
/// \code /// \code
/// // Load a texture /// // Load a texture
/// const auto texture = sf::Texture::createFromFile("texture.png").value(); /// const sf::Texture texture("texture.png");
/// ///
/// // Create a sprite /// // Create a sprite
/// sf::Sprite sprite(texture); /// sf::Sprite sprite(texture);

View File

@ -469,7 +469,7 @@ private:
/// Usage example: /// Usage example:
/// \code /// \code
/// // Open a font /// // Open a font
/// const auto font = sf::Font::createFromFile("arial.ttf").value(); /// const sf::Font font("arial.ttf");
/// ///
/// // Create a text /// // Create a text
/// sf::Text text(font, "hello"); /// sf::Text text(font, "hello");

View File

@ -37,7 +37,6 @@
#include <SFML/System/Vector2.hpp> #include <SFML/System/Vector2.hpp>
#include <filesystem> #include <filesystem>
#include <optional>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
@ -61,6 +60,8 @@ public:
/// ///
/// Creates a texture with width 0 and height 0. /// Creates a texture with width 0 and height 0.
/// ///
/// \see resize
///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Texture(); Texture();
@ -96,6 +97,174 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Texture& operator=(Texture&&) noexcept; Texture& operator=(Texture&&) noexcept;
////////////////////////////////////////////////////////////
/// \brief Construct the texture from a file on disk
///
/// The maximum size for a texture depends on the graphics
/// driver and can be retrieved with the getMaximumSize function.
///
/// \param filename Path of the image file to load
/// \param sRgb True to enable sRGB conversion, false to disable it
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream, loadFromImage
///
////////////////////////////////////////////////////////////
explicit Texture(const std::filesystem::path& filename, bool sRgb = false);
////////////////////////////////////////////////////////////
/// \brief Construct the texture from a sub-rectangle of a file on disk
///
/// The \a area argument can be used to load only a sub-rectangle
/// of the whole image. If you want the entire image then leave
/// the default value (which is an empty IntRect).
/// If the \a area rectangle crosses the bounds of the image, it
/// is adjusted to fit the image size.
///
/// The maximum size for a texture depends on the graphics
/// driver and can be retrieved with the getMaximumSize function.
///
/// \param filename Path of the image file to load
/// \param sRgb True to enable sRGB conversion, false to disable it
/// \param area Area of the image to load
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream, loadFromImage
///
////////////////////////////////////////////////////////////
Texture(const std::filesystem::path& filename, bool sRgb, const IntRect& area);
////////////////////////////////////////////////////////////
/// \brief Construct the texture from a file in memory
///
/// The maximum size for a texture depends on the graphics
/// driver and can be retrieved with the getMaximumSize function.
///
/// \param data Pointer to the file data in memory
/// \param size Size of the data to load, in bytes
/// \param sRgb True to enable sRGB conversion, false to disable it
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream, loadFromImage
///
////////////////////////////////////////////////////////////
Texture(const void* data, std::size_t size, bool sRgb = false);
////////////////////////////////////////////////////////////
/// \brief Construct the texture from a sub-rectangle of a file in memory
///
/// The \a area argument can be used to load only a sub-rectangle
/// of the whole image. If you want the entire image then leave
/// the default value (which is an empty IntRect).
/// If the \a area rectangle crosses the bounds of the image, it
/// is adjusted to fit the image size.
///
/// The maximum size for a texture depends on the graphics
/// driver and can be retrieved with the getMaximumSize function.
///
/// \param data Pointer to the file data in memory
/// \param size Size of the data to load, in bytes
/// \param sRgb True to enable sRGB conversion, false to disable it
/// \param area Area of the image to load
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream, loadFromImage
///
////////////////////////////////////////////////////////////
Texture(const void* data, std::size_t size, bool sRgb, const IntRect& area);
////////////////////////////////////////////////////////////
/// \brief Construct the texture from a custom stream
///
/// The maximum size for a texture depends on the graphics
/// driver and can be retrieved with the getMaximumSize function.
///
/// \param stream Source stream to read from
/// \param sRgb True to enable sRGB conversion, false to disable it
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream, loadFromImage
///
////////////////////////////////////////////////////////////
explicit Texture(InputStream& stream, bool sRgb = false);
////////////////////////////////////////////////////////////
/// \brief Construct the texture from a sub-rectangle of a custom stream
///
/// The \a area argument can be used to load only a sub-rectangle
/// of the whole image. If you want the entire image then leave
/// the default value (which is an empty IntRect).
/// If the \a area rectangle crosses the bounds of the image, it
/// is adjusted to fit the image size.
///
/// The maximum size for a texture depends on the graphics
/// driver and can be retrieved with the getMaximumSize function.
///
/// \param stream Source stream to read from
/// \param sRgb True to enable sRGB conversion, false to disable it
/// \param area Area of the image to load
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream, loadFromImage
///
////////////////////////////////////////////////////////////
Texture(InputStream& stream, bool sRgb, const IntRect& area);
////////////////////////////////////////////////////////////
/// \brief Construct the texture from an image
///
/// The maximum size for a texture depends on the graphics
/// driver and can be retrieved with the getMaximumSize function.
///
/// \param image Image to load into the texture
/// \param sRgb True to enable sRGB conversion, false to disable it
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream, loadFromImage
///
////////////////////////////////////////////////////////////
explicit Texture(const Image& image, bool sRgb = false);
////////////////////////////////////////////////////////////
/// \brief Construct the texture from a sub-rectangle of an image
///
/// The \a area argument is used to load only a sub-rectangle
/// of the whole image.
/// If the \a area rectangle crosses the bounds of the image, it
/// is adjusted to fit the image size.
///
/// The maximum size for a texture depends on the graphics
/// driver and can be retrieved with the getMaximumSize function.
///
/// \param image Image to load into the texture
/// \param sRgb True to enable sRGB conversion, false to disable it
/// \param area Area of the image to load
///
/// \throws std::runtime_error if loading was unsuccessful
///
/// \see loadFromFile, loadFromMemory, loadFromStream, loadFromImage
///
////////////////////////////////////////////////////////////
Texture(const Image& image, bool sRgb, const IntRect& area);
////////////////////////////////////////////////////////////
/// \brief Construct the texture with a given size
///
/// \param size Width and height of the texture
/// \param sRgb True to enable sRGB conversion, false to disable it
///
/// \throws std::runtime_error if construction was unsuccessful
///
////////////////////////////////////////////////////////////
explicit Texture(Vector2u size, bool sRgb = false);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Resize the texture /// \brief Resize the texture
/// ///
@ -104,7 +273,7 @@ public:
/// \param size Width and height of the texture /// \param size Width and height of the texture
/// \param sRgb True to enable sRGB conversion, false to disable it /// \param sRgb True to enable sRGB conversion, false to disable it
/// ///
/// \return True if resizing was successful /// \return True if resizing was successful, false if it failed
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool resize(Vector2u size, bool sRgb = false); [[nodiscard]] bool resize(Vector2u size, bool sRgb = false);
@ -112,15 +281,6 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Load the texture from a file on disk /// \brief Load the texture from a file on disk
/// ///
/// This function is a shortcut for the following code:
/// \code
/// sf::Image image;
/// if (!image.loadFromFile(filename))
/// return false;
/// if (!texture.loadFromImage(image, area))
/// return false;
/// \endcode
///
/// The \a area argument can be used to load only a sub-rectangle /// The \a area argument can be used to load only a sub-rectangle
/// of the whole image. If you want the entire image then leave /// of the whole image. If you want the entire image then leave
/// the default value (which is an empty IntRect). /// the default value (which is an empty IntRect).
@ -136,25 +296,16 @@ public:
/// \param sRgb True to enable sRGB conversion, false to disable it /// \param sRgb True to enable sRGB conversion, false to disable it
/// \param area Area of the image to load /// \param area Area of the image to load
/// ///
/// \return True if loading was successful /// \return True if loading was successful, false if it failed
/// ///
/// \see loadFromMemory, loadFromStream, loadFromImage /// \see loadFromMemory, loadFromStream, loadFromImage
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromFile(const std::filesystem::path& filename, bool sRgb = false, const IntRect& area = IntRect()); [[nodiscard]] bool loadFromFile(const std::filesystem::path& filename, bool sRgb = false, const IntRect& area = {});
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Load the texture from a file in memory /// \brief Load the texture from a file in memory
/// ///
/// This function is a shortcut for the following code:
/// \code
/// sf::Image image;
/// if (!image.loadFromMemory(data, size))
/// return false;
/// if (!texture.loadFromImage(image, area))
/// return false;
/// \endcode
///
/// The \a area argument can be used to load only a sub-rectangle /// The \a area argument can be used to load only a sub-rectangle
/// of the whole image. If you want the entire image then leave /// of the whole image. If you want the entire image then leave
/// the default value (which is an empty IntRect). /// the default value (which is an empty IntRect).
@ -171,25 +322,16 @@ public:
/// \param sRgb True to enable sRGB conversion, false to disable it /// \param sRgb True to enable sRGB conversion, false to disable it
/// \param area Area of the image to load /// \param area Area of the image to load
/// ///
/// \return True if loading was successful /// \return True if loading was successful, false if it failed
/// ///
/// \see loadFromFile, loadFromStream, loadFromImage /// \see loadFromFile, loadFromStream, loadFromImage
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromMemory(const void* data, std::size_t size, bool sRgb = false, const IntRect& area = IntRect()); [[nodiscard]] bool loadFromMemory(const void* data, std::size_t size, bool sRgb = false, const IntRect& area = {});
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Load the texture from a custom stream /// \brief Load the texture from a custom stream
/// ///
/// This function is a shortcut for the following code:
/// \code
/// sf::Image image;
/// if (!image.loadFromStream(stream))
/// return false;
/// if (!texture.loadFromImage(image, area))
/// return false;
/// \endcode
///
/// The \a area argument can be used to load only a sub-rectangle /// The \a area argument can be used to load only a sub-rectangle
/// of the whole image. If you want the entire image then leave /// of the whole image. If you want the entire image then leave
/// the default value (which is an empty IntRect). /// the default value (which is an empty IntRect).
@ -205,12 +347,12 @@ public:
/// \param sRgb True to enable sRGB conversion, false to disable it /// \param sRgb True to enable sRGB conversion, false to disable it
/// \param area Area of the image to load /// \param area Area of the image to load
/// ///
/// \return True if loading was successful /// \return True if loading was successful, false if it failed
/// ///
/// \see loadFromFile, loadFromMemory, loadFromImage /// \see loadFromFile, loadFromMemory, loadFromImage
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromStream(InputStream& stream, bool sRgb = false, const IntRect& area = IntRect()); [[nodiscard]] bool loadFromStream(InputStream& stream, bool sRgb = false, const IntRect& area = {});
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Load the texture from an image /// \brief Load the texture from an image
@ -230,132 +372,12 @@ public:
/// \param sRgb True to enable sRGB conversion, false to disable it /// \param sRgb True to enable sRGB conversion, false to disable it
/// \param area Area of the image to load /// \param area Area of the image to load
/// ///
/// \return True if loading was successful /// \return True if loading was successful, false if it failed
/// ///
/// \see loadFromFile, loadFromMemory /// \see loadFromFile, loadFromMemory
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromImage(const Image& image, bool sRgb = false, const IntRect& area = IntRect()); [[nodiscard]] bool loadFromImage(const Image& image, bool sRgb = false, const IntRect& area = {});
////////////////////////////////////////////////////////////
/// \brief Create a texture
///
/// \param size Width and height of the texture
/// \param sRgb True to enable sRGB conversion, false to disable it
///
/// \return Texture if creation was successful, otherwise `std::nullopt`
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Texture> create(Vector2u size, bool sRgb = false);
////////////////////////////////////////////////////////////
/// \brief Load the texture from a file on disk
///
/// The \a area argument can be used to load only a sub-rectangle
/// of the whole image. If you want the entire image then leave
/// the default value (which is an empty IntRect).
/// If the \a area rectangle crosses the bounds of the image, it
/// is adjusted to fit the image size.
///
/// The maximum size for a texture depends on the graphics
/// driver and can be retrieved with the getMaximumSize function.
///
/// If this function fails, the texture is left unchanged.
///
/// \param filename Path of the image file to load
/// \param sRgb True to enable sRGB conversion, false to disable it
/// \param area Area of the image to load
///
/// \return Texture if loading was successful, otherwise `std::nullopt`
///
/// \see createFromMemory, createFromStream, createFromImage
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Texture> createFromFile(const std::filesystem::path& filename,
bool sRgb = false,
const IntRect& area = {});
////////////////////////////////////////////////////////////
/// \brief Load the texture from a file in memory
///
/// The \a area argument can be used to load only a sub-rectangle
/// of the whole image. If you want the entire image then leave
/// the default value (which is an empty IntRect).
/// If the \a area rectangle crosses the bounds of the image, it
/// is adjusted to fit the image size.
///
/// The maximum size for a texture depends on the graphics
/// driver and can be retrieved with the getMaximumSize function.
///
/// If this function fails, the texture is left unchanged.
///
/// \param data Pointer to the file data in memory
/// \param size Size of the data to load, in bytes
/// \param sRgb True to enable sRGB conversion, false to disable it
/// \param area Area of the image to load
///
/// \return Texture if loading was successful, otherwise `std::nullopt`
///
/// \see createFromFile, createFromStream, createFromImage
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Texture> createFromMemory(
const void* data,
std::size_t size,
bool sRgb = false,
const IntRect& area = {});
////////////////////////////////////////////////////////////
/// \brief Load the texture from a custom stream
///
/// The \a area argument can be used to load only a sub-rectangle
/// of the whole image. If you want the entire image then leave
/// the default value (which is an empty IntRect).
/// If the \a area rectangle crosses the bounds of the image, it
/// is adjusted to fit the image size.
///
/// The maximum size for a texture depends on the graphics
/// driver and can be retrieved with the getMaximumSize function.
///
/// If this function fails, the texture is left unchanged.
///
/// \param stream Source stream to read from
/// \param sRgb True to enable sRGB conversion, false to disable it
/// \param area Area of the image to load
///
/// \return Texture if loading was successful, otherwise `std::nullopt`
///
/// \see createFromFile, createFromMemory, createFromImage
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Texture> createFromStream(InputStream& stream,
bool sRgb = false,
const IntRect& area = {});
////////////////////////////////////////////////////////////
/// \brief Load the texture from an image
///
/// The \a area argument can be used to load only a sub-rectangle
/// of the whole image. If you want the entire image then leave
/// the default value (which is an empty IntRect).
/// If the \a area rectangle crosses the bounds of the image, it
/// is adjusted to fit the image size.
///
/// The maximum size for a texture depends on the graphics
/// driver and can be retrieved with the getMaximumSize function.
///
/// If this function fails, the texture is left unchanged.
///
/// \param image Image to load into the texture
/// \param sRgb True to enable sRGB conversion, false to disable it
/// \param area Area of the image to load
///
/// \return Texture if loading was successful, otherwise `std::nullopt`
///
/// \see createFromFile, createFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Texture> createFromImage(const Image& image, bool sRgb = false, const IntRect& area = {});
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Return the size of the texture /// \brief Return the size of the texture
@ -375,7 +397,7 @@ public:
/// ///
/// \return Image containing the texture's pixels /// \return Image containing the texture's pixels
/// ///
/// \see createFromImage /// \see loadFromImage
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] Image copyToImage() const; [[nodiscard]] Image copyToImage() const;
@ -771,7 +793,7 @@ SFML_GRAPHICS_API void swap(Texture& left, Texture& right) noexcept;
/// However, if you want to perform some modifications on the pixels /// However, if you want to perform some modifications on the pixels
/// before creating the final texture, you can load your file to a /// before creating the final texture, you can load your file to a
/// sf::Image, do whatever you need with the pixels, and then call /// sf::Image, do whatever you need with the pixels, and then call
/// Texture::createFromImage. /// Texture(const Image&).
/// ///
/// Since they live in the graphics card memory, the pixels of a texture /// Since they live in the graphics card memory, the pixels of a texture
/// cannot be accessed without a slow copy first. And they cannot be /// cannot be accessed without a slow copy first. And they cannot be
@ -803,7 +825,7 @@ SFML_GRAPHICS_API void swap(Texture& left, Texture& right) noexcept;
/// // drawing a sprite /// // drawing a sprite
/// ///
/// // Load a texture from a file /// // Load a texture from a file
/// const auto texture = sf::Texture::createFromFile("texture.png").value(); /// const sf::Texture texture("texture.png");
/// ///
/// // Assign it to a sprite /// // Assign it to a sprite
/// sf::Sprite sprite(texture); /// sf::Sprite sprite(texture);
@ -817,7 +839,7 @@ SFML_GRAPHICS_API void swap(Texture& left, Texture& right) noexcept;
/// // streaming real-time data, like video frames /// // streaming real-time data, like video frames
/// ///
/// // Create an empty texture /// // Create an empty texture
/// auto texture = sf::Texture::create({640, 480}).value(); /// sf::Texture texture({640, 480});
/// ///
/// // Create a sprite that will display the texture /// // Create a sprite that will display the texture
/// sf::Sprite sprite(texture); /// sf::Sprite sprite(texture);

View File

@ -95,6 +95,16 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
FileInputStream& operator=(FileInputStream&&) noexcept; FileInputStream& operator=(FileInputStream&&) noexcept;
////////////////////////////////////////////////////////////
/// \brief Construct the stream from a file path
///
/// \param filename Name of the file to open
///
/// \throws std::runtime_error on error
///
////////////////////////////////////////////////////////////
explicit FileInputStream(const std::filesystem::path& filename);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Open the stream from a file path /// \brief Open the stream from a file path
/// ///
@ -105,16 +115,6 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool open(const std::filesystem::path& filename); [[nodiscard]] bool open(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Create the stream from a file path
///
/// \param filename Name of the file to open
///
/// \return File input stream on success, `std::nullopt` on error
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<FileInputStream> create(const std::filesystem::path& filename);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Read data from the stream /// \brief Read data from the stream
/// ///

View File

@ -103,12 +103,12 @@ public:
/// from which SFML can load resources. /// from which SFML can load resources.
/// ///
/// SFML resource classes like sf::Texture and /// SFML resource classes like sf::Texture and
/// sf::SoundBuffer provide createFromFile and createFromMemory functions, /// sf::SoundBuffer provide loadFromFile and loadFromMemory functions,
/// which read data from conventional sources. However, if you /// which read data from conventional sources. However, if you
/// have data coming from a different source (over a network, /// have data coming from a different source (over a network,
/// embedded, encrypted, compressed, etc) you can derive your /// embedded, encrypted, compressed, etc) you can derive your
/// own class from sf::InputStream and load SFML resources with /// own class from sf::InputStream and load SFML resources with
/// their createFromStream function. /// their loadFromStream function.
/// ///
/// Usage example: /// Usage example:
/// \code /// \code
@ -142,7 +142,7 @@ public:
/// // Handle error... /// // Handle error...
/// } /// }
/// ///
/// const auto texture = sf::Texture::createFromStream(stream).value(); /// const sf::Texture texture(stream);
/// ///
/// // musics... /// // musics...
/// sf::Music music; /// sf::Music music;

View File

@ -55,15 +55,6 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
MemoryInputStream(const void* data, std::size_t sizeInBytes); MemoryInputStream(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Open the stream from its data
///
/// \param data Pointer to the data in memory
/// \param sizeInBytes Size of the data, in bytes
///
////////////////////////////////////////////////////////////
void open(const void* data, std::size_t sizeInBytes);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Read data from the stream /// \brief Read data from the stream
/// ///

View File

@ -144,6 +144,55 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Cursor& operator=(Cursor&&) noexcept; Cursor& operator=(Cursor&&) noexcept;
////////////////////////////////////////////////////////////
/// \brief Construct a cursor with the provided image
///
/// \a pixels must be an array of \a width by \a height pixels
/// in 32-bit RGBA format. If not, this will cause undefined behavior.
///
/// If \a pixels is null or either \a width or \a height are 0,
/// the current cursor is left unchanged and the function will
/// return false.
///
/// In addition to specifying the pixel data, you can also
/// specify the location of the hotspot of the cursor. The
/// hotspot is the pixel coordinate within the cursor image
/// which will be located exactly where the mouse pointer
/// position is. Any mouse actions that are performed will
/// return the window/screen location of the hotspot.
///
/// \warning On Unix platforms which do not support colored
/// cursors, the pixels are mapped into a monochrome
/// bitmap: pixels with an alpha channel to 0 are
/// transparent, black if the RGB channel are close
/// to zero, and white otherwise.
///
/// \param pixels Array of pixels of the image
/// \param size Width and height of the image
/// \param hotspot (x,y) location of the hotspot
///
/// \throws std::runtime_error if the cursor could not be constructed
///
////////////////////////////////////////////////////////////
Cursor(const std::uint8_t* pixels, Vector2u size, Vector2u hotspot);
////////////////////////////////////////////////////////////
/// \brief Create a native system cursor
///
/// Refer to the list of cursor available on each system
/// (see sf::Cursor::Type) to know whether a given cursor is
/// expected to load successfully or is not supported by
/// the operating system.
///
/// \param type Native system cursor type
///
/// \throws std::runtime_error if the corresponding cursor
/// is not natively supported by the operating
/// system
///
////////////////////////////////////////////////////////////
explicit Cursor(Type type);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Create a cursor with the provided image /// \brief Create a cursor with the provided image
/// ///

View File

@ -89,7 +89,7 @@ public:
const String& title, const String& title,
std::uint32_t style = Style::Default, std::uint32_t style = Style::Default,
State state = State::Windowed, State state = State::Windowed,
const ContextSettings& settings = ContextSettings()); const ContextSettings& settings = {});
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Construct a new window /// \brief Construct a new window
@ -108,7 +108,7 @@ public:
/// \param settings Additional settings for the underlying OpenGL context /// \param settings Additional settings for the underlying OpenGL context
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Window(VideoMode mode, const String& title, State state, const ContextSettings& settings = ContextSettings()); Window(VideoMode mode, const String& title, State state, const ContextSettings& settings = {});
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Construct the window from an existing control /// \brief Construct the window from an existing control
@ -124,7 +124,7 @@ public:
/// \param settings Additional settings for the underlying OpenGL context /// \param settings Additional settings for the underlying OpenGL context
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
explicit Window(WindowHandle handle, const ContextSettings& settings = ContextSettings()); explicit Window(WindowHandle handle, const ContextSettings& settings = {});
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Destructor /// \brief Destructor

View File

@ -66,6 +66,30 @@ void InputSoundFile::StreamDeleter::operator()(InputStream* ptr) const
} }
////////////////////////////////////////////////////////////
InputSoundFile::InputSoundFile(const std::filesystem::path& filename)
{
if (!openFromFile(filename))
throw std::runtime_error("Failed to open input sound file");
}
////////////////////////////////////////////////////////////
InputSoundFile::InputSoundFile(const void* data, std::size_t sizeInBytes)
{
if (!openFromMemory(data, sizeInBytes))
throw std::runtime_error("Failed to open input sound file from memory");
}
////////////////////////////////////////////////////////////
InputSoundFile::InputSoundFile(InputStream& stream)
{
if (!openFromStream(stream))
throw std::runtime_error("Failed to open input sound file from stream");
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool InputSoundFile::openFromFile(const std::filesystem::path& filename) bool InputSoundFile::openFromFile(const std::filesystem::path& filename)
{ {
@ -192,42 +216,6 @@ bool InputSoundFile::openFromStream(InputStream& stream)
} }
////////////////////////////////////////////////////////////
std::optional<InputSoundFile> InputSoundFile::createFromFile(const std::filesystem::path& filename)
{
auto inputSoundFile = std::make_optional<InputSoundFile>();
if (!inputSoundFile->openFromFile(filename))
return std::nullopt;
return inputSoundFile;
}
////////////////////////////////////////////////////////////
std::optional<InputSoundFile> InputSoundFile::createFromMemory(const void* data, std::size_t sizeInBytes)
{
auto inputSoundFile = std::make_optional<InputSoundFile>();
if (!inputSoundFile->openFromMemory(data, sizeInBytes))
return std::nullopt;
return inputSoundFile;
}
////////////////////////////////////////////////////////////
std::optional<InputSoundFile> InputSoundFile::createFromStream(InputStream& stream)
{
auto inputSoundFile = std::make_optional<InputSoundFile>();
if (!inputSoundFile->openFromStream(stream))
return std::nullopt;
return inputSoundFile;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::uint64_t InputSoundFile::getSampleCount() const std::uint64_t InputSoundFile::getSampleCount() const
{ {

View File

@ -64,6 +64,30 @@ Music::Music() : m_impl(std::make_unique<Impl>())
} }
////////////////////////////////////////////////////////////
Music::Music(const std::filesystem::path& filename) : Music()
{
if (!openFromFile(filename))
throw std::runtime_error("Failed to open music from file");
}
////////////////////////////////////////////////////////////
Music::Music(const void* data, std::size_t sizeInBytes) : Music()
{
if (!openFromMemory(data, sizeInBytes))
throw std::runtime_error("Failed to open music from memory");
}
////////////////////////////////////////////////////////////
Music::Music(InputStream& stream) : Music()
{
if (!openFromStream(stream))
throw std::runtime_error("Failed to open music from stream");
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Music::~Music() Music::~Music()
{ {
@ -152,42 +176,6 @@ bool Music::openFromStream(InputStream& stream)
} }
////////////////////////////////////////////////////////////
std::optional<Music> Music::createFromFile(const std::filesystem::path& filename)
{
auto music = std::make_optional<Music>();
if (!music->openFromFile(filename))
return std::nullopt;
return music;
}
////////////////////////////////////////////////////////////
std::optional<Music> Music::createFromMemory(const void* data, std::size_t sizeInBytes)
{
auto music = std::make_optional<Music>();
if (!music->openFromMemory(data, sizeInBytes))
return std::nullopt;
return music;
}
////////////////////////////////////////////////////////////
std::optional<Music> Music::createFromStream(InputStream& stream)
{
auto music = std::make_optional<Music>();
if (!music->openFromStream(stream))
return std::nullopt;
return music;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Time Music::getDuration() const Time Music::getDuration() const
{ {

View File

@ -36,6 +36,17 @@
namespace sf namespace sf
{ {
////////////////////////////////////////////////////////////
OutputSoundFile::OutputSoundFile(const std::filesystem::path& filename,
unsigned int sampleRate,
unsigned int channelCount,
const std::vector<SoundChannel>& channelMap)
{
if (!openFromFile(filename, sampleRate, channelCount, channelMap))
throw std::runtime_error("Failed to open output sound file");
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool OutputSoundFile::openFromFile(const std::filesystem::path& filename, bool OutputSoundFile::openFromFile(const std::filesystem::path& filename,
unsigned int sampleRate, unsigned int sampleRate,
@ -65,22 +76,6 @@ bool OutputSoundFile::openFromFile(const std::filesystem::path& filename,
} }
////////////////////////////////////////////////////////////
std::optional<OutputSoundFile> OutputSoundFile::createFromFile(
const std::filesystem::path& filename,
unsigned int sampleRate,
unsigned int channelCount,
const std::vector<SoundChannel>& channelMap)
{
auto outputSoundFile = std::make_optional<OutputSoundFile>();
if (!outputSoundFile->openFromFile(filename, sampleRate, channelCount, channelMap))
return std::nullopt;
return outputSoundFile;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void OutputSoundFile::write(const std::int16_t* samples, std::uint64_t count) void OutputSoundFile::write(const std::int16_t* samples, std::uint64_t count)
{ {

View File

@ -39,6 +39,42 @@
namespace sf namespace sf
{ {
////////////////////////////////////////////////////////////
SoundBuffer::SoundBuffer(const std::filesystem::path& filename)
{
if (!loadFromFile(filename))
throw std::runtime_error("Failed to open sound buffer from file");
}
////////////////////////////////////////////////////////////
SoundBuffer::SoundBuffer(const void* data, std::size_t sizeInBytes)
{
if (!loadFromMemory(data, sizeInBytes))
throw std::runtime_error("Failed to open sound buffer from memory");
}
////////////////////////////////////////////////////////////
SoundBuffer::SoundBuffer(InputStream& stream)
{
if (!loadFromStream(stream))
throw std::runtime_error("Failed to open sound buffer from stream");
}
////////////////////////////////////////////////////////////
SoundBuffer::SoundBuffer(const std::int16_t* samples,
std::uint64_t sampleCount,
unsigned int channelCount,
unsigned int sampleRate,
const std::vector<SoundChannel>& channelMap)
{
if (!loadFromSamples(samples, sampleCount, channelCount, sampleRate, channelMap))
throw std::runtime_error("Failed to open sound buffer from samples");
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
SoundBuffer::SoundBuffer(const SoundBuffer& copy) SoundBuffer::SoundBuffer(const SoundBuffer& copy)
{ {
@ -130,59 +166,6 @@ bool SoundBuffer::loadFromSamples(const std::int16_t* samples,
} }
////////////////////////////////////////////////////////////
std::optional<SoundBuffer> SoundBuffer::createFromFile(const std::filesystem::path& filename)
{
auto soundBuffer = std::make_optional<SoundBuffer>();
if (!soundBuffer->loadFromFile(filename))
return std::nullopt;
return soundBuffer;
}
////////////////////////////////////////////////////////////
std::optional<SoundBuffer> SoundBuffer::createFromMemory(const void* data, std::size_t sizeInBytes)
{
auto soundBuffer = std::make_optional<SoundBuffer>();
if (!soundBuffer->loadFromMemory(data, sizeInBytes))
return std::nullopt;
return soundBuffer;
}
////////////////////////////////////////////////////////////
std::optional<SoundBuffer> SoundBuffer::createFromStream(InputStream& stream)
{
auto soundBuffer = std::make_optional<SoundBuffer>();
if (!soundBuffer->loadFromStream(stream))
return std::nullopt;
return soundBuffer;
}
////////////////////////////////////////////////////////////
std::optional<SoundBuffer> SoundBuffer::createFromSamples(
const std::int16_t* samples,
std::uint64_t sampleCount,
unsigned int channelCount,
unsigned int sampleRate,
const std::vector<SoundChannel>& channelMap)
{
auto soundBuffer = std::make_optional<SoundBuffer>();
if (!soundBuffer->loadFromSamples(samples, sampleCount, channelCount, sampleRate, channelMap))
return std::nullopt;
return soundBuffer;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool SoundBuffer::saveToFile(const std::filesystem::path& filename) const bool SoundBuffer::saveToFile(const std::filesystem::path& filename) const
{ {

View File

@ -48,7 +48,7 @@ SoundBufferRecorder::~SoundBufferRecorder()
bool SoundBufferRecorder::onStart() bool SoundBufferRecorder::onStart()
{ {
m_samples.clear(); m_samples.clear();
m_buffer = SoundBuffer(); m_buffer = {};
return true; return true;
} }

View File

@ -120,6 +120,30 @@ struct Font::FontHandles
}; };
////////////////////////////////////////////////////////////
Font::Font(const std::filesystem::path& filename)
{
if (!openFromFile(filename))
throw std::runtime_error("Failed to open font from file");
}
////////////////////////////////////////////////////////////
Font::Font(const void* data, std::size_t sizeInBytes)
{
if (!openFromMemory(data, sizeInBytes))
throw std::runtime_error("Failed to open font from memory");
}
////////////////////////////////////////////////////////////
Font::Font(InputStream& stream)
{
if (!openFromStream(stream))
throw std::runtime_error("Failed to open font from stream");
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool Font::openFromFile(const std::filesystem::path& filename) bool Font::openFromFile(const std::filesystem::path& filename)
{ {
@ -252,7 +276,7 @@ bool Font::openFromStream(InputStream& stream)
} }
// Make sure that the stream's reading position is at the beginning // Make sure that the stream's reading position is at the beginning
if (!stream.seek(0)) if (!stream.seek(0).has_value())
{ {
err() << "Failed to seek font stream" << std::endl; err() << "Failed to seek font stream" << std::endl;
return false; return false;
@ -305,42 +329,6 @@ bool Font::openFromStream(InputStream& stream)
} }
////////////////////////////////////////////////////////////
std::optional<Font> Font::createFromFile(const std::filesystem::path& filename)
{
auto font = std::make_optional<Font>();
if (!font->openFromFile(filename))
return std::nullopt;
return font;
}
////////////////////////////////////////////////////////////
std::optional<Font> Font::createFromMemory(const void* data, std::size_t sizeInBytes)
{
auto font = std::make_optional<Font>();
if (!font->openFromMemory(data, sizeInBytes))
return std::nullopt;
return font;
}
////////////////////////////////////////////////////////////
std::optional<Font> Font::createFromStream(InputStream& stream)
{
auto font = std::make_optional<Font>();
if (!font->openFromStream(stream))
return std::nullopt;
return font;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
const Font::Info& Font::getInfo() const const Font::Info& Font::getInfo() const
{ {
@ -409,8 +397,7 @@ float Font::getKerning(std::uint32_t first, std::uint32_t second, unsigned int c
// Combine kerning with compensation deltas and return the X advance // Combine kerning with compensation deltas and return the X advance
// Flooring is required as we use FT_KERNING_UNFITTED flag which is not quantized in 64 based grid // Flooring is required as we use FT_KERNING_UNFITTED flag which is not quantized in 64 based grid
return std::floor( return std::floor((secondLsbDelta - firstRsbDelta + static_cast<float>(kerning.x) + 32) / float{1 << 6});
(secondLsbDelta - firstRsbDelta + static_cast<float>(kerning.x) + 32) / static_cast<float>(1 << 6));
} }
// Invalid font // Invalid font
@ -425,7 +412,7 @@ float Font::getLineSpacing(unsigned int characterSize) const
if (face && setCurrentSize(characterSize)) if (face && setCurrentSize(characterSize))
{ {
return static_cast<float>(face->size->metrics.height) / static_cast<float>(1 << 6); return static_cast<float>(face->size->metrics.height) / float{1 << 6};
} }
return 0.f; return 0.f;
@ -443,8 +430,7 @@ float Font::getUnderlinePosition(unsigned int characterSize) const
if (!FT_IS_SCALABLE(face)) if (!FT_IS_SCALABLE(face))
return static_cast<float>(characterSize) / 10.f; return static_cast<float>(characterSize) / 10.f;
return -static_cast<float>(FT_MulFix(face->underline_position, face->size->metrics.y_scale)) / return -static_cast<float>(FT_MulFix(face->underline_position, face->size->metrics.y_scale)) / float{1 << 6};
static_cast<float>(1 << 6);
} }
return 0.f; return 0.f;
@ -462,8 +448,7 @@ float Font::getUnderlineThickness(unsigned int characterSize) const
if (!FT_IS_SCALABLE(face)) if (!FT_IS_SCALABLE(face))
return static_cast<float>(characterSize) / 14.f; return static_cast<float>(characterSize) / 14.f;
return static_cast<float>(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale)) / return static_cast<float>(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale)) / float{1 << 6};
static_cast<float>(1 << 6);
} }
return 0.f; return 0.f;
@ -563,7 +548,7 @@ Glyph Font::loadGlyph(std::uint32_t codePoint, unsigned int characterSize, bool
FT_Stroker stroker = m_fontHandles->stroker; FT_Stroker stroker = m_fontHandles->stroker;
FT_Stroker_Set(stroker, FT_Stroker_Set(stroker,
static_cast<FT_Fixed>(outlineThickness * static_cast<float>(1 << 6)), static_cast<FT_Fixed>(outlineThickness * float{1 << 6}),
FT_STROKER_LINECAP_ROUND, FT_STROKER_LINECAP_ROUND,
FT_STROKER_LINEJOIN_ROUND, FT_STROKER_LINEJOIN_ROUND,
0); 0);
@ -591,47 +576,41 @@ Glyph Font::loadGlyph(std::uint32_t codePoint, unsigned int characterSize, bool
// Compute the glyph's advance offset // Compute the glyph's advance offset
glyph.advance = static_cast<float>(bitmapGlyph->root.advance.x >> 16); glyph.advance = static_cast<float>(bitmapGlyph->root.advance.x >> 16);
if (bold) if (bold)
glyph.advance += static_cast<float>(weight) / static_cast<float>(1 << 6); glyph.advance += static_cast<float>(weight) / float{1 << 6};
glyph.lsbDelta = static_cast<int>(face->glyph->lsb_delta); glyph.lsbDelta = static_cast<int>(face->glyph->lsb_delta);
glyph.rsbDelta = static_cast<int>(face->glyph->rsb_delta); glyph.rsbDelta = static_cast<int>(face->glyph->rsb_delta);
unsigned int width = bitmap.width; Vector2u size(bitmap.width, bitmap.rows);
unsigned int height = bitmap.rows;
if ((width > 0) && (height > 0)) if ((size.x > 0) && (size.y > 0))
{ {
// Leave a small padding around characters, so that filtering doesn't // Leave a small padding around characters, so that filtering doesn't
// pollute them with pixels from neighbors // pollute them with pixels from neighbors
const unsigned int padding = 2; const unsigned int padding = 2;
width += 2 * padding; size += 2u * Vector2u(padding, padding);
height += 2 * padding;
// Get the glyphs page corresponding to the character size // Get the glyphs page corresponding to the character size
Page& page = loadPage(characterSize); Page& page = loadPage(characterSize);
// Find a good position for the new glyph into the texture // Find a good position for the new glyph into the texture
glyph.textureRect = findGlyphRect(page, {width, height}); glyph.textureRect = findGlyphRect(page, size);
// Make sure the texture data is positioned in the center // Make sure the texture data is positioned in the center
// of the allocated texture rectangle // of the allocated texture rectangle
glyph.textureRect.position.x += static_cast<int>(padding); glyph.textureRect.position += Vector2i(padding, padding);
glyph.textureRect.position.y += static_cast<int>(padding); glyph.textureRect.size -= 2 * Vector2i(padding, padding);
glyph.textureRect.size.x -= static_cast<int>(2 * padding);
glyph.textureRect.size.y -= static_cast<int>(2 * padding);
// Compute the glyph's bounding box // Compute the glyph's bounding box
glyph.bounds.position.x = static_cast<float>(bitmapGlyph->left); glyph.bounds.position = Vector2f(Vector2i(bitmapGlyph->left, -bitmapGlyph->top));
glyph.bounds.position.y = static_cast<float>(-bitmapGlyph->top); glyph.bounds.size = Vector2f(Vector2u(bitmap.width, bitmap.rows));
glyph.bounds.size.x = static_cast<float>(bitmap.width);
glyph.bounds.size.y = static_cast<float>(bitmap.rows);
// Resize the pixel buffer to the new size and fill it with transparent white pixels // Resize the pixel buffer to the new size and fill it with transparent white pixels
m_pixelBuffer.resize(static_cast<std::size_t>(width) * static_cast<std::size_t>(height) * 4); m_pixelBuffer.resize(static_cast<std::size_t>(size.x) * static_cast<std::size_t>(size.y) * 4);
std::uint8_t* current = m_pixelBuffer.data(); std::uint8_t* current = m_pixelBuffer.data();
std::uint8_t* end = current + width * height * 4; std::uint8_t* end = current + size.x * size.y * 4;
while (current != end) while (current != end)
{ {
@ -646,12 +625,12 @@ Glyph Font::loadGlyph(std::uint32_t codePoint, unsigned int characterSize, bool
if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
{ {
// Pixels are 1 bit monochrome values // Pixels are 1 bit monochrome values
for (unsigned int y = padding; y < height - padding; ++y) for (unsigned int y = padding; y < size.y - padding; ++y)
{ {
for (unsigned int x = padding; x < width - padding; ++x) for (unsigned int x = padding; x < size.x - padding; ++x)
{ {
// The color channels remain white, just fill the alpha channel // The color channels remain white, just fill the alpha channel
const std::size_t index = x + y * width; const std::size_t index = x + y * size.x;
m_pixelBuffer[index * 4 + 3] = ((pixels[(x - padding) / 8]) & (1 << (7 - ((x - padding) % 8)))) ? 255 : 0; m_pixelBuffer[index * 4 + 3] = ((pixels[(x - padding) / 8]) & (1 << (7 - ((x - padding) % 8)))) ? 255 : 0;
} }
pixels += bitmap.pitch; pixels += bitmap.pitch;
@ -659,13 +638,13 @@ Glyph Font::loadGlyph(std::uint32_t codePoint, unsigned int characterSize, bool
} }
else else
{ {
// Pixels are 8 bits gray levels // Pixels are 8 bit gray levels
for (unsigned int y = padding; y < height - padding; ++y) for (unsigned int y = padding; y < size.y - padding; ++y)
{ {
for (unsigned int x = padding; x < width - padding; ++x) for (unsigned int x = padding; x < size.x - padding; ++x)
{ {
// The color channels remain white, just fill the alpha channel // The color channels remain white, just fill the alpha channel
const std::size_t index = x + y * width; const std::size_t index = x + y * size.x;
m_pixelBuffer[index * 4 + 3] = pixels[x - padding]; m_pixelBuffer[index * 4 + 3] = pixels[x - padding];
} }
pixels += bitmap.pitch; pixels += bitmap.pitch;
@ -673,11 +652,9 @@ Glyph Font::loadGlyph(std::uint32_t codePoint, unsigned int characterSize, bool
} }
// Write the pixels to the texture // Write the pixels to the texture
const unsigned int x = static_cast<unsigned int>(glyph.textureRect.position.x) - padding; const auto dest = Vector2u(glyph.textureRect.position) - Vector2u(padding, padding);
const unsigned int y = static_cast<unsigned int>(glyph.textureRect.position.y) - padding; const auto updateSize = Vector2u(glyph.textureRect.size) + 2u * Vector2u(padding, padding);
const unsigned int w = static_cast<unsigned int>(glyph.textureRect.size.x) + 2 * padding; page.texture.update(m_pixelBuffer.data(), updateSize, dest);
const unsigned int h = static_cast<unsigned int>(glyph.textureRect.size.y) + 2 * padding;
page.texture.update(m_pixelBuffer.data(), {w, h}, {x, y});
} }
// Delete the FT glyph // Delete the FT glyph

View File

@ -109,6 +109,30 @@ Image::Image(Vector2u size, const std::uint8_t* pixels)
} }
////////////////////////////////////////////////////////////
Image::Image(const std::filesystem::path& filename)
{
if (!loadFromFile(filename))
throw std::runtime_error("Failed to open image from file");
}
////////////////////////////////////////////////////////////
Image::Image(const void* data, std::size_t size)
{
if (!loadFromMemory(data, size))
throw std::runtime_error("Failed to open image from memory");
}
////////////////////////////////////////////////////////////
Image::Image(InputStream& stream)
{
if (!loadFromStream(stream))
throw std::runtime_error("Failed to open image from stream");
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void Image::resize(Vector2u size, Color color) void Image::resize(Vector2u size, Color color)
{ {
@ -257,7 +281,7 @@ bool Image::loadFromStream(InputStream& stream)
m_pixels.clear(); m_pixels.clear();
// Make sure that the stream's reading position is at the beginning // Make sure that the stream's reading position is at the beginning
if (!stream.seek(0)) if (!stream.seek(0).has_value())
{ {
err() << "Failed to seek image stream" << std::endl; err() << "Failed to seek image stream" << std::endl;
return false; return false;
@ -292,42 +316,6 @@ bool Image::loadFromStream(InputStream& stream)
} }
////////////////////////////////////////////////////////////
std::optional<Image> Image::createFromFile(const std::filesystem::path& filename)
{
auto image = std::make_optional<Image>();
if (!image->loadFromFile(filename))
return std::nullopt;
return image;
}
////////////////////////////////////////////////////////////
std::optional<Image> Image::createFromMemory(const void* data, std::size_t size)
{
auto image = std::make_optional<Image>();
if (!image->loadFromMemory(data, size))
return std::nullopt;
return image;
}
////////////////////////////////////////////////////////////
std::optional<Image> Image::createFromStream(InputStream& stream)
{
auto image = std::make_optional<Image>();
if (!image->loadFromStream(stream))
return std::nullopt;
return image;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool Image::saveToFile(const std::filesystem::path& filename) const bool Image::saveToFile(const std::filesystem::path& filename) const
{ {

View File

@ -43,6 +43,14 @@ namespace sf
RenderTexture::RenderTexture() = default; RenderTexture::RenderTexture() = default;
////////////////////////////////////////////////////////////
RenderTexture::RenderTexture(Vector2u size, const ContextSettings& settings)
{
if (!resize(size, settings))
throw std::runtime_error("Failed to create render texture");
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
RenderTexture::~RenderTexture() = default; RenderTexture::~RenderTexture() = default;
@ -96,18 +104,6 @@ bool RenderTexture::resize(Vector2u size, const ContextSettings& settings)
} }
////////////////////////////////////////////////////////////
std::optional<RenderTexture> RenderTexture::create(Vector2u size, const ContextSettings& settings)
{
auto renderTexture = std::make_optional<RenderTexture>();
if (!renderTexture->resize(size, settings))
return std::nullopt;
return renderTexture;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
unsigned int RenderTexture::getMaximumAntialiasingLevel() unsigned int RenderTexture::getMaximumAntialiasingLevel()
{ {
@ -169,8 +165,9 @@ bool RenderTexture::setActive(bool active)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void RenderTexture::display() void RenderTexture::display()
{ {
if (m_impl) if (!m_impl)
{ return;
if (priv::RenderTextureImplFBO::isAvailable()) if (priv::RenderTextureImplFBO::isAvailable())
{ {
// Perform a RenderTarget-only activation if we are using FBOs // Perform a RenderTarget-only activation if we are using FBOs
@ -188,7 +185,6 @@ void RenderTexture::display()
m_impl->updateTexture(m_texture.m_texture); m_impl->updateTexture(m_texture.m_texture);
m_texture.m_pixelsFlipped = true; m_texture.m_pixelsFlipped = true;
m_texture.invalidateMipmap(); m_texture.invalidateMipmap();
}
} }
@ -202,7 +198,7 @@ Vector2u RenderTexture::getSize() const
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool RenderTexture::isSrgb() const bool RenderTexture::isSrgb() const
{ {
assert(m_impl && "Must call RenderTexture::create first"); assert(m_impl && "RenderTexture::isSrgb() Must first initialize render texture");
return m_impl->isSrgb(); return m_impl->isSrgb();
} }

View File

@ -223,6 +223,80 @@ struct Shader::UniformBinder
}; };
////////////////////////////////////////////////////////////
Shader::Shader(const std::filesystem::path& filename, Type type)
{
if (!loadFromFile(filename, type))
throw std::runtime_error("Failed to load shader from file");
}
////////////////////////////////////////////////////////////
Shader::Shader(const std::filesystem::path& vertexShaderFilename, const std::filesystem::path& fragmentShaderFilename)
{
if (!loadFromFile(vertexShaderFilename, fragmentShaderFilename))
throw std::runtime_error("Failed to load shader from files");
}
////////////////////////////////////////////////////////////
Shader::Shader(const std::filesystem::path& vertexShaderFilename,
const std::filesystem::path& geometryShaderFilename,
const std::filesystem::path& fragmentShaderFilename)
{
if (!loadFromFile(vertexShaderFilename, geometryShaderFilename, fragmentShaderFilename))
throw std::runtime_error("Failed to load shader from files");
}
////////////////////////////////////////////////////////////
Shader::Shader(std::string_view shader, Type type)
{
if (!loadFromMemory(shader, type))
throw std::runtime_error("Failed to load shader from memory");
}
////////////////////////////////////////////////////////////
Shader::Shader(std::string_view vertexShader, std::string_view fragmentShader)
{
if (!loadFromMemory(vertexShader, fragmentShader))
throw std::runtime_error("Failed to load shader from memory");
}
////////////////////////////////////////////////////////////
Shader::Shader(std::string_view vertexShader, std::string_view geometryShader, std::string_view fragmentShader)
{
if (!loadFromMemory(vertexShader, geometryShader, fragmentShader))
throw std::runtime_error("Failed to load shader from memory");
}
////////////////////////////////////////////////////////////
Shader::Shader(InputStream& stream, Type type)
{
if (!loadFromStream(stream, type))
throw std::runtime_error("Failed to load shader from stream");
}
////////////////////////////////////////////////////////////
Shader::Shader(InputStream& vertexShaderStream, InputStream& fragmentShaderStream)
{
if (!loadFromStream(vertexShaderStream, fragmentShaderStream))
throw std::runtime_error("Failed to load shader from streams");
}
////////////////////////////////////////////////////////////
Shader::Shader(InputStream& vertexShaderStream, InputStream& geometryShaderStream, InputStream& fragmentShaderStream)
{
if (!loadFromStream(vertexShaderStream, geometryShaderStream, fragmentShaderStream))
throw std::runtime_error("Failed to load shader from streams");
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Shader::~Shader() Shader::~Shader()
{ {
@ -455,121 +529,6 @@ bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& geomet
} }
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromFile(const std::filesystem::path& filename, Type type)
{
auto shader = std::make_optional<Shader>();
if (!shader->loadFromFile(filename, type))
return std::nullopt;
return shader;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromFile(const std::filesystem::path& vertexShaderFilename,
const std::filesystem::path& fragmentShaderFilename)
{
auto shader = std::make_optional<Shader>();
if (!shader->loadFromFile(vertexShaderFilename, fragmentShaderFilename))
return std::nullopt;
return shader;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromFile(const std::filesystem::path& vertexShaderFilename,
const std::filesystem::path& geometryShaderFilename,
const std::filesystem::path& fragmentShaderFilename)
{
auto shader = std::make_optional<Shader>();
if (!shader->loadFromFile(vertexShaderFilename, geometryShaderFilename, fragmentShaderFilename))
return std::nullopt;
return shader;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromMemory(std::string_view shader, Type type)
{
auto newShader = std::make_optional<Shader>();
if (!newShader->loadFromMemory(shader, type))
return std::nullopt;
return newShader;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromMemory(std::string_view vertexShader, std::string_view fragmentShader)
{
auto shader = std::make_optional<Shader>();
if (!shader->loadFromMemory(vertexShader, fragmentShader))
return std::nullopt;
return shader;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromMemory(std::string_view vertexShader,
std::string_view geometryShader,
std::string_view fragmentShader)
{
auto shader = std::make_optional<Shader>();
if (!shader->loadFromMemory(vertexShader, geometryShader, fragmentShader))
return std::nullopt;
return shader;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromStream(InputStream& stream, Type type)
{
auto shader = std::make_optional<Shader>();
if (!shader->loadFromStream(stream, type))
return std::nullopt;
return shader;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromStream(InputStream& vertexShaderStream, InputStream& fragmentShaderStream)
{
auto shader = std::make_optional<Shader>();
if (!shader->loadFromStream(vertexShaderStream, fragmentShaderStream))
return std::nullopt;
return shader;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromStream(InputStream& vertexShaderStream,
InputStream& geometryShaderStream,
InputStream& fragmentShaderStream)
{
auto shader = std::make_optional<Shader>();
if (!shader->loadFromStream(vertexShaderStream, geometryShaderStream, fragmentShaderStream))
return std::nullopt;
return shader;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void Shader::setUniform(const std::string& name, float x) void Shader::setUniform(const std::string& name, float x)
{ {
@ -691,8 +650,9 @@ void Shader::setUniform(const std::string& name, const Glsl::Mat4& matrix)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void Shader::setUniform(const std::string& name, const Texture& texture) void Shader::setUniform(const std::string& name, const Texture& texture)
{ {
if (m_shaderProgram) if (!m_shaderProgram)
{ return;
const TransientContextLock lock; const TransientContextLock lock;
// Find the location of the variable in the shader // Find the location of the variable in the shader
@ -719,20 +679,19 @@ void Shader::setUniform(const std::string& name, const Texture& texture)
it->second = &texture; it->second = &texture;
} }
} }
}
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void Shader::setUniform(const std::string& name, CurrentTextureType) void Shader::setUniform(const std::string& name, CurrentTextureType)
{ {
if (m_shaderProgram) if (!m_shaderProgram)
{ return;
const TransientContextLock lock; const TransientContextLock lock;
// Find the location of the variable in the shader // Find the location of the variable in the shader
m_currentTexture = getUniformLocation(name); m_currentTexture = getUniformLocation(name);
}
} }
@ -1077,6 +1036,74 @@ int Shader::getUniformLocation(const std::string& name)
namespace sf namespace sf
{ {
////////////////////////////////////////////////////////////
Shader::Shader(const std::filesystem::path& /* filename */, Type /* type */)
{
throw std::runtime_error("Shaders are not supported with OpenGL ES 1");
}
////////////////////////////////////////////////////////////
Shader::Shader(const std::filesystem::path& /* vertexShaderFilename */,
const std::filesystem::path& /* fragmentShaderFilename */)
{
throw std::runtime_error("Shaders are not supported with OpenGL ES 1");
}
////////////////////////////////////////////////////////////
Shader::Shader(const std::filesystem::path& /* vertexShaderFilename */,
const std::filesystem::path& /* geometryShaderFilename */,
const std::filesystem::path& /* fragmentShaderFilename */)
{
throw std::runtime_error("Shaders are not supported with OpenGL ES 1");
}
////////////////////////////////////////////////////////////
Shader::Shader(std::string_view /* shader */, Type /* type */)
{
throw std::runtime_error("Shaders are not supported with OpenGL ES 1");
}
////////////////////////////////////////////////////////////
Shader::Shader(std::string_view /* vertexShader */, std::string_view /* fragmentShader */)
{
throw std::runtime_error("Shaders are not supported with OpenGL ES 1");
}
////////////////////////////////////////////////////////////
Shader::Shader(std::string_view /* vertexShader */, std::string_view /* geometryShader */, std::string_view /* fragmentShader */)
{
throw std::runtime_error("Shaders are not supported with OpenGL ES 1");
}
////////////////////////////////////////////////////////////
Shader::Shader(InputStream& /* stream */, Type /* type */)
{
throw std::runtime_error("Shaders are not supported with OpenGL ES 1");
}
////////////////////////////////////////////////////////////
Shader::Shader(InputStream& /* vertexShaderStream */, InputStream& /* fragmentShaderStream */)
{
throw std::runtime_error("Shaders are not supported with OpenGL ES 1");
}
////////////////////////////////////////////////////////////
Shader::Shader(InputStream& /* vertexShaderStream */,
InputStream& /* geometryShaderStream */,
InputStream& /* fragmentShaderStream */)
{
throw std::runtime_error("Shaders are not supported with OpenGL ES 1");
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Shader::~Shader() = default; Shader::~Shader() = default;
@ -1159,76 +1186,6 @@ bool Shader::loadFromStream(InputStream& /* vertexShaderStream */,
} }
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromFile(const std::filesystem::path& /* filename */, Type /* type */)
{
return std::nullopt;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromFile(const std::filesystem::path& /* vertexShaderFilename */,
const std::filesystem::path& /* fragmentShaderFilename */)
{
return std::nullopt;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromFile(const std::filesystem::path& /* vertexShaderFilename */,
const std::filesystem::path& /* geometryShaderFilename */,
const std::filesystem::path& /* fragmentShaderFilename */)
{
return std::nullopt;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromMemory(std::string_view /* shader */, Type /* type */)
{
return std::nullopt;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromMemory(std::string_view /* vertexShader */, std::string_view /* fragmentShader */)
{
return std::nullopt;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromMemory(std::string_view /* vertexShader */,
std::string_view /* geometryShader */,
std::string_view /* fragmentShader */)
{
return std::nullopt;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromStream(InputStream& /* stream */, Type /* type */)
{
return std::nullopt;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromStream(InputStream& /* vertexShaderStream */, InputStream& /* fragmentShaderStream */)
{
return std::nullopt;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromStream(InputStream& /* vertexShaderStream */,
InputStream& /* geometryShaderStream */,
InputStream& /* fragmentShaderStream */)
{
return std::nullopt;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void Shader::setUniform(const std::string& /* name */, float) void Shader::setUniform(const std::string& /* name */, float)
{ {

View File

@ -71,6 +71,78 @@ Texture::Texture() : m_cacheId(TextureImpl::getUniqueId())
} }
////////////////////////////////////////////////////////////
Texture::Texture(const std::filesystem::path& filename, bool sRgb) : Texture()
{
if (!loadFromFile(filename, sRgb))
throw std::runtime_error("Failed to load texture from file");
}
////////////////////////////////////////////////////////////
Texture::Texture(const std::filesystem::path& filename, bool sRgb, const IntRect& area) : Texture()
{
if (!loadFromFile(filename, sRgb, area))
throw std::runtime_error("Failed to load texture from file");
}
////////////////////////////////////////////////////////////
Texture::Texture(const void* data, std::size_t size, bool sRgb) : Texture()
{
if (!loadFromMemory(data, size, sRgb))
throw std::runtime_error("Failed to load texture from memory");
}
////////////////////////////////////////////////////////////
Texture::Texture(const void* data, std::size_t size, bool sRgb, const IntRect& area) : Texture()
{
if (!loadFromMemory(data, size, sRgb, area))
throw std::runtime_error("Failed to load texture from memory");
}
////////////////////////////////////////////////////////////
Texture::Texture(InputStream& stream, bool sRgb) : Texture()
{
if (!loadFromStream(stream, sRgb))
throw std::runtime_error("Failed to load texture from stream");
}
////////////////////////////////////////////////////////////
Texture::Texture(InputStream& stream, bool sRgb, const IntRect& area) : Texture()
{
if (!loadFromStream(stream, sRgb, area))
throw std::runtime_error("Failed to load texture from stream");
}
////////////////////////////////////////////////////////////
Texture::Texture(const Image& image, bool sRgb) : Texture()
{
if (!loadFromImage(image, sRgb))
throw std::runtime_error("Failed to load texture from image");
}
////////////////////////////////////////////////////////////
Texture::Texture(const Image& image, bool sRgb, const IntRect& area) : Texture()
{
if (!loadFromImage(image, sRgb, area))
throw std::runtime_error("Failed to load texture from image");
}
////////////////////////////////////////////////////////////
Texture::Texture(Vector2u size, bool sRgb) : Texture()
{
if (!resize(size, sRgb))
throw std::runtime_error("Failed to create texture");
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Texture::Texture(const Texture& copy) : Texture::Texture(const Texture& copy) :
GlResource(copy), GlResource(copy),
@ -361,66 +433,6 @@ bool Texture::loadFromImage(const Image& image, bool sRgb, const IntRect& area)
} }
////////////////////////////////////////////////////////////
std::optional<Texture> Texture::create(Vector2u size, bool sRgb)
{
auto texture = std::make_optional<Texture>();
if (!texture->resize(size, sRgb))
return std::nullopt;
return texture;
}
////////////////////////////////////////////////////////////
std::optional<Texture> Texture::createFromFile(const std::filesystem::path& filename, bool sRgb, const IntRect& area)
{
auto texture = std::make_optional<Texture>();
if (!texture->loadFromFile(filename, sRgb, area))
return std::nullopt;
return texture;
}
////////////////////////////////////////////////////////////
std::optional<Texture> Texture::createFromMemory(const void* data, std::size_t size, bool sRgb, const IntRect& area)
{
auto texture = std::make_optional<Texture>();
if (!texture->loadFromMemory(data, size, sRgb, area))
return std::nullopt;
return texture;
}
////////////////////////////////////////////////////////////
std::optional<Texture> Texture::createFromStream(InputStream& stream, bool sRgb, const IntRect& area)
{
auto texture = std::make_optional<Texture>();
if (!texture->loadFromStream(stream, sRgb, area))
return std::nullopt;
return texture;
}
////////////////////////////////////////////////////////////
std::optional<Texture> Texture::createFromImage(const Image& image, bool sRgb, const IntRect& area)
{
auto texture = std::make_optional<Texture>();
if (!texture->loadFromImage(image, sRgb, area))
return std::nullopt;
return texture;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Vector2u Texture::getSize() const Vector2u Texture::getSize() const
{ {

View File

@ -47,6 +47,14 @@ void FileInputStream::FileCloser::operator()(std::FILE* file)
FileInputStream::FileInputStream() = default; FileInputStream::FileInputStream() = default;
////////////////////////////////////////////////////////////
FileInputStream::FileInputStream(const std::filesystem::path& filename)
{
if (!open(filename))
throw std::runtime_error("Failed to open file input stream");
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
FileInputStream::~FileInputStream() = default; FileInputStream::~FileInputStream() = default;
@ -78,18 +86,6 @@ bool FileInputStream::open(const std::filesystem::path& filename)
} }
////////////////////////////////////////////////////////////
std::optional<FileInputStream> FileInputStream::create(const std::filesystem::path& filename)
{
auto fileInputStream = std::make_optional<FileInputStream>();
if (!fileInputStream->open(filename))
return std::nullopt;
return fileInputStream;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::optional<std::size_t> FileInputStream::read(void* data, std::size_t size) std::optional<std::size_t> FileInputStream::read(void* data, std::size_t size)
{ {

View File

@ -35,18 +35,10 @@
namespace sf namespace sf
{ {
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
MemoryInputStream::MemoryInputStream(const void* data, std::size_t sizeInBytes) MemoryInputStream::MemoryInputStream(const void* data, std::size_t sizeInBytes) :
m_data(static_cast<const std::byte*>(data)),
m_size(sizeInBytes)
{ {
open(data, sizeInBytes);
}
////////////////////////////////////////////////////////////
void MemoryInputStream::open(const void* data, std::size_t sizeInBytes)
{
m_data = static_cast<const std::byte*>(data);
m_size = sizeInBytes;
m_offset = 0;
} }

View File

@ -43,6 +43,25 @@ Cursor::Cursor() : m_impl(std::make_unique<priv::CursorImpl>())
} }
////////////////////////////////////////////////////////////
Cursor::Cursor(const std::uint8_t* pixels, Vector2u size, Vector2u hotspot) : Cursor()
{
if ((pixels == nullptr) || (size.x == 0) || (size.y == 0))
throw std::runtime_error("Failed to create cursor from pixels (invalid arguments)");
if (!m_impl->loadFromPixels(pixels, size, hotspot))
throw std::runtime_error("Failed to create cursor from pixels");
}
////////////////////////////////////////////////////////////
Cursor::Cursor(Type type) : Cursor()
{
if (!m_impl->loadFromSystem(type))
throw std::runtime_error("Failed to create cursor from type");
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Cursor::~Cursor() = default; Cursor::~Cursor() = default;

View File

@ -22,6 +22,8 @@ TEST_CASE("[Audio] sf::InputSoundFile")
} }
SECTION("Construction") SECTION("Construction")
{
SECTION("Default constructor")
{ {
const sf::InputSoundFile inputSoundFile; const sf::InputSoundFile inputSoundFile;
CHECK(inputSoundFile.getSampleCount() == 0); CHECK(inputSoundFile.getSampleCount() == 0);
@ -32,6 +34,122 @@ TEST_CASE("[Audio] sf::InputSoundFile")
CHECK(inputSoundFile.getSampleOffset() == 0); CHECK(inputSoundFile.getSampleOffset() == 0);
} }
SECTION("Invalid file")
{
CHECK_THROWS_AS(sf::InputSoundFile("does/not/exist.wav"), std::runtime_error);
}
SECTION("Valid file")
{
SECTION("flac")
{
const sf::InputSoundFile inputSoundFile("Audio/ding.flac");
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 44'100);
CHECK(inputSoundFile.getDuration() == sf::microseconds(1'990'884));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
SECTION("mp3")
{
const sf::InputSoundFile inputSoundFile("Audio/ding.mp3");
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 44'100);
CHECK(inputSoundFile.getDuration() == sf::microseconds(1'990'884));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
SECTION("ogg")
{
const sf::InputSoundFile inputSoundFile("Audio/doodle_pop.ogg");
CHECK(inputSoundFile.getSampleCount() == 2'116'992);
CHECK(inputSoundFile.getChannelCount() == 2);
CHECK(inputSoundFile.getSampleRate() == 44'100);
CHECK(inputSoundFile.getDuration() == sf::microseconds(24'002'176));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
SECTION("wav")
{
const sf::InputSoundFile inputSoundFile("Audio/killdeer.wav");
CHECK(inputSoundFile.getSampleCount() == 112'941);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 22'050);
CHECK(inputSoundFile.getDuration() == sf::microseconds(5'122'040));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
}
SECTION("Memory")
{
const auto memory = loadIntoMemory("Audio/killdeer.wav");
const sf::InputSoundFile inputSoundFile(memory.data(), memory.size());
CHECK(inputSoundFile.getSampleCount() == 112'941);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 22'050);
CHECK(inputSoundFile.getDuration() == sf::microseconds(5'122'040));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
SECTION("Stream")
{
SECTION("flac")
{
sf::FileInputStream stream("Audio/ding.flac");
const sf::InputSoundFile inputSoundFile(stream);
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 44'100);
CHECK(inputSoundFile.getDuration() == sf::microseconds(1'990'884));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
SECTION("mp3")
{
sf::FileInputStream stream("Audio/ding.mp3");
const sf::InputSoundFile inputSoundFile(stream);
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 44'100);
CHECK(inputSoundFile.getDuration() == sf::microseconds(1'990'884));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
SECTION("ogg")
{
sf::FileInputStream stream("Audio/doodle_pop.ogg");
const sf::InputSoundFile inputSoundFile(stream);
CHECK(inputSoundFile.getSampleCount() == 2'116'992);
CHECK(inputSoundFile.getChannelCount() == 2);
CHECK(inputSoundFile.getSampleRate() == 44'100);
CHECK(inputSoundFile.getDuration() == sf::microseconds(24'002'176));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
SECTION("wav")
{
sf::FileInputStream stream("Audio/killdeer.wav");
const sf::InputSoundFile inputSoundFile(stream);
CHECK(inputSoundFile.getSampleCount() == 112'941);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 22'050);
CHECK(inputSoundFile.getDuration() == sf::microseconds(5'122'040));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
}
}
SECTION("openFromFile()") SECTION("openFromFile()")
{ {
sf::InputSoundFile inputSoundFile; sf::InputSoundFile inputSoundFile;
@ -164,129 +282,11 @@ TEST_CASE("[Audio] sf::InputSoundFile")
} }
} }
SECTION("createFromFile()")
{
SECTION("Invalid file")
{
CHECK(!sf::InputSoundFile::createFromFile("does/not/exist.wav"));
}
SECTION("Valid file")
{
SECTION("flac")
{
const auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/ding.flac").value();
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 44'100);
CHECK(inputSoundFile.getDuration() == sf::microseconds(1'990'884));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
SECTION("mp3")
{
const auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/ding.mp3").value();
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 44'100);
CHECK(inputSoundFile.getDuration() == sf::microseconds(1'990'884));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
SECTION("ogg")
{
const auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/doodle_pop.ogg").value();
CHECK(inputSoundFile.getSampleCount() == 2'116'992);
CHECK(inputSoundFile.getChannelCount() == 2);
CHECK(inputSoundFile.getSampleRate() == 44'100);
CHECK(inputSoundFile.getDuration() == sf::microseconds(24'002'176));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
SECTION("wav")
{
const auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/killdeer.wav").value();
CHECK(inputSoundFile.getSampleCount() == 112'941);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 22'050);
CHECK(inputSoundFile.getDuration() == sf::microseconds(5'122'040));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
}
}
SECTION("createFromMemory()")
{
const auto memory = loadIntoMemory("Audio/killdeer.wav");
const auto inputSoundFile = sf::InputSoundFile::createFromMemory(memory.data(), memory.size()).value();
CHECK(inputSoundFile.getSampleCount() == 112'941);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 22'050);
CHECK(inputSoundFile.getDuration() == sf::microseconds(5'122'040));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
SECTION("createFromStream()")
{
SECTION("flac")
{
auto stream = sf::FileInputStream::create("Audio/ding.flac").value();
const auto inputSoundFile = sf::InputSoundFile::createFromStream(stream).value();
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 44'100);
CHECK(inputSoundFile.getDuration() == sf::microseconds(1'990'884));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
SECTION("mp3")
{
auto stream = sf::FileInputStream::create("Audio/ding.mp3").value();
const auto inputSoundFile = sf::InputSoundFile::createFromStream(stream).value();
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 44'100);
CHECK(inputSoundFile.getDuration() == sf::microseconds(1'990'884));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
SECTION("ogg")
{
auto stream = sf::FileInputStream::create("Audio/doodle_pop.ogg").value();
const auto inputSoundFile = sf::InputSoundFile::createFromStream(stream).value();
CHECK(inputSoundFile.getSampleCount() == 2'116'992);
CHECK(inputSoundFile.getChannelCount() == 2);
CHECK(inputSoundFile.getSampleRate() == 44'100);
CHECK(inputSoundFile.getDuration() == sf::microseconds(24'002'176));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
SECTION("wav")
{
auto stream = sf::FileInputStream::create("Audio/killdeer.wav").value();
const auto inputSoundFile = sf::InputSoundFile::createFromStream(stream).value();
CHECK(inputSoundFile.getSampleCount() == 112'941);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 22'050);
CHECK(inputSoundFile.getDuration() == sf::microseconds(5'122'040));
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
}
SECTION("seek(std::uint64_t)") SECTION("seek(std::uint64_t)")
{ {
SECTION("flac") SECTION("flac")
{ {
auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/ding.flac").value(); sf::InputSoundFile inputSoundFile("Audio/ding.flac");
inputSoundFile.seek(1'000); inputSoundFile.seek(1'000);
CHECK(inputSoundFile.getTimeOffset() == sf::microseconds(22'675)); CHECK(inputSoundFile.getTimeOffset() == sf::microseconds(22'675));
CHECK(inputSoundFile.getSampleOffset() == 1'000); CHECK(inputSoundFile.getSampleOffset() == 1'000);
@ -294,7 +294,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("mp3") SECTION("mp3")
{ {
auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/ding.mp3").value(); sf::InputSoundFile inputSoundFile("Audio/ding.mp3");
inputSoundFile.seek(1'000); inputSoundFile.seek(1'000);
CHECK(inputSoundFile.getTimeOffset() == sf::microseconds(22'675)); CHECK(inputSoundFile.getTimeOffset() == sf::microseconds(22'675));
CHECK(inputSoundFile.getSampleOffset() == 1'000); CHECK(inputSoundFile.getSampleOffset() == 1'000);
@ -302,7 +302,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("ogg") SECTION("ogg")
{ {
auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/doodle_pop.ogg").value(); sf::InputSoundFile inputSoundFile("Audio/doodle_pop.ogg");
inputSoundFile.seek(1'000); inputSoundFile.seek(1'000);
CHECK(inputSoundFile.getTimeOffset() == sf::microseconds(11'337)); CHECK(inputSoundFile.getTimeOffset() == sf::microseconds(11'337));
CHECK(inputSoundFile.getSampleOffset() == 1'000); CHECK(inputSoundFile.getSampleOffset() == 1'000);
@ -310,7 +310,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("wav") SECTION("wav")
{ {
auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/killdeer.wav").value(); sf::InputSoundFile inputSoundFile("Audio/killdeer.wav");
inputSoundFile.seek(1'000); inputSoundFile.seek(1'000);
CHECK(inputSoundFile.getTimeOffset() == sf::microseconds(45'351)); CHECK(inputSoundFile.getTimeOffset() == sf::microseconds(45'351));
CHECK(inputSoundFile.getSampleOffset() == 1'000); CHECK(inputSoundFile.getSampleOffset() == 1'000);
@ -319,7 +319,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("seek(Time)") SECTION("seek(Time)")
{ {
auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/ding.flac").value(); sf::InputSoundFile inputSoundFile("Audio/ding.flac");
inputSoundFile.seek(sf::milliseconds(100)); inputSoundFile.seek(sf::milliseconds(100));
CHECK(inputSoundFile.getSampleCount() == 87'798); CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1); CHECK(inputSoundFile.getChannelCount() == 1);
@ -331,7 +331,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("read()") SECTION("read()")
{ {
auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/ding.flac").value(); sf::InputSoundFile inputSoundFile("Audio/ding.flac");
SECTION("Null address") SECTION("Null address")
{ {
@ -349,7 +349,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
{ {
SECTION("flac") SECTION("flac")
{ {
inputSoundFile = sf::InputSoundFile::createFromFile("Audio/ding.flac").value(); inputSoundFile = sf::InputSoundFile("Audio/ding.flac");
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4); CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
CHECK(samples == std::array<std::int16_t, 4>{0, 1, -1, 4}); CHECK(samples == std::array<std::int16_t, 4>{0, 1, -1, 4});
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4); CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
@ -358,7 +358,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("mp3") SECTION("mp3")
{ {
inputSoundFile = sf::InputSoundFile::createFromFile("Audio/ding.mp3").value(); inputSoundFile = sf::InputSoundFile("Audio/ding.mp3");
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4); CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
CHECK(samples == std::array<std::int16_t, 4>{0, -2, 0, 2}); CHECK(samples == std::array<std::int16_t, 4>{0, -2, 0, 2});
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4); CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
@ -367,7 +367,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("ogg") SECTION("ogg")
{ {
inputSoundFile = sf::InputSoundFile::createFromFile("Audio/doodle_pop.ogg").value(); inputSoundFile = sf::InputSoundFile("Audio/doodle_pop.ogg");
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4); CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
CHECK(samples == std::array<std::int16_t, 4>{-827, -985, -1168, -1319}); CHECK(samples == std::array<std::int16_t, 4>{-827, -985, -1168, -1319});
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4); CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
@ -383,7 +383,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("close()") SECTION("close()")
{ {
auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/ding.flac").value(); sf::InputSoundFile inputSoundFile("Audio/ding.flac");
inputSoundFile.close(); inputSoundFile.close();
CHECK(inputSoundFile.getSampleCount() == 0); CHECK(inputSoundFile.getSampleCount() == 0);
CHECK(inputSoundFile.getChannelCount() == 0); CHECK(inputSoundFile.getChannelCount() == 0);

View File

@ -32,7 +32,9 @@ TEST_CASE("[Audio] sf::Music", runAudioDeviceTests())
CHECK(timeSpan.length == sf::Time::Zero); CHECK(timeSpan.length == sf::Time::Zero);
} }
SECTION("Construction") SECTION("Constructor")
{
SECTION("Default constructor")
{ {
const sf::Music music; const sf::Music music;
CHECK(music.getDuration() == sf::Time::Zero); CHECK(music.getDuration() == sf::Time::Zero);
@ -46,6 +48,70 @@ TEST_CASE("[Audio] sf::Music", runAudioDeviceTests())
CHECK(!music.getLoop()); CHECK(!music.getLoop());
} }
SECTION("File")
{
SECTION("Invalid file")
{
CHECK_THROWS_AS(sf::Music("does/not/exist.wav"), std::runtime_error);
}
SECTION("Valid file")
{
const sf::Music music("Audio/ding.mp3");
CHECK(music.getDuration() == sf::microseconds(1990884));
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::Time::Zero);
CHECK(length == sf::microseconds(1990884));
CHECK(music.getChannelCount() == 1);
CHECK(music.getSampleRate() == 44100);
CHECK(music.getStatus() == sf::Music::Status::Stopped);
CHECK(music.getPlayingOffset() == sf::Time::Zero);
CHECK(!music.getLoop());
}
}
SECTION("Memory")
{
std::vector<std::byte> memory(10, std::byte{0xCA});
SECTION("Invalid buffer")
{
CHECK_THROWS_AS(sf::Music(memory.data(), memory.size()), std::runtime_error);
}
SECTION("Valid buffer")
{
memory = loadIntoMemory("Audio/ding.flac");
const sf::Music music(memory.data(), memory.size());
CHECK(music.getDuration() == sf::microseconds(1990884));
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::Time::Zero);
CHECK(length == sf::microseconds(1990884));
CHECK(music.getChannelCount() == 1);
CHECK(music.getSampleRate() == 44100);
CHECK(music.getStatus() == sf::Music::Status::Stopped);
CHECK(music.getPlayingOffset() == sf::Time::Zero);
CHECK(!music.getLoop());
}
}
SECTION("Stream")
{
sf::FileInputStream stream("Audio/doodle_pop.ogg");
const sf::Music music(stream);
CHECK(music.getDuration() == sf::microseconds(24002176));
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::Time::Zero);
CHECK(length == sf::microseconds(24002176));
CHECK(music.getChannelCount() == 2);
CHECK(music.getSampleRate() == 44100);
CHECK(music.getStatus() == sf::Music::Status::Stopped);
CHECK(music.getPlayingOffset() == sf::Time::Zero);
CHECK(!music.getLoop());
}
}
SECTION("openFromFile()") SECTION("openFromFile()")
{ {
sf::Music music; sf::Music music;
@ -149,72 +215,9 @@ TEST_CASE("[Audio] sf::Music", runAudioDeviceTests())
} }
} }
SECTION("createFromFile()")
{
SECTION("Invalid file")
{
CHECK(!sf::Music::createFromFile("does/not/exist.wav"));
}
SECTION("Valid file")
{
const auto music = sf::Music::createFromFile("Audio/ding.mp3").value();
CHECK(music.getDuration() == sf::microseconds(1990884));
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::Time::Zero);
CHECK(length == sf::microseconds(1990884));
CHECK(music.getChannelCount() == 1);
CHECK(music.getSampleRate() == 44100);
CHECK(music.getStatus() == sf::Music::Status::Stopped);
CHECK(music.getPlayingOffset() == sf::Time::Zero);
CHECK(!music.getLoop());
}
}
SECTION("createFromMemory()")
{
std::vector<std::byte> memory(10, std::byte{0xCA});
SECTION("Invalid buffer")
{
CHECK(!sf::Music::createFromMemory(memory.data(), memory.size()));
}
SECTION("Valid buffer")
{
memory = loadIntoMemory("Audio/ding.flac");
const auto music = sf::Music::createFromMemory(memory.data(), memory.size()).value();
CHECK(music.getDuration() == sf::microseconds(1990884));
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::Time::Zero);
CHECK(length == sf::microseconds(1990884));
CHECK(music.getChannelCount() == 1);
CHECK(music.getSampleRate() == 44100);
CHECK(music.getStatus() == sf::Music::Status::Stopped);
CHECK(music.getPlayingOffset() == sf::Time::Zero);
CHECK(!music.getLoop());
}
}
SECTION("createFromStream()")
{
auto stream = sf::FileInputStream::create("Audio/doodle_pop.ogg").value();
const auto music = sf::Music::createFromStream(stream).value();
CHECK(music.getDuration() == sf::microseconds(24002176));
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::Time::Zero);
CHECK(length == sf::microseconds(24002176));
CHECK(music.getChannelCount() == 2);
CHECK(music.getSampleRate() == 44100);
CHECK(music.getStatus() == sf::Music::Status::Stopped);
CHECK(music.getPlayingOffset() == sf::Time::Zero);
CHECK(!music.getLoop());
}
SECTION("play/pause/stop") SECTION("play/pause/stop")
{ {
auto music = sf::Music::createFromFile("Audio/ding.mp3").value(); sf::Music music("Audio/ding.mp3");
// Wait for background thread to start // Wait for background thread to start
music.play(); music.play();
@ -237,7 +240,7 @@ TEST_CASE("[Audio] sf::Music", runAudioDeviceTests())
SECTION("setLoopPoints()") SECTION("setLoopPoints()")
{ {
auto music = sf::Music::createFromFile("Audio/killdeer.wav").value(); sf::Music music("Audio/killdeer.wav");
music.setLoopPoints({sf::seconds(1), sf::seconds(2)}); music.setLoopPoints({sf::seconds(1), sf::seconds(2)});
const auto [offset, length] = music.getLoopPoints(); const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::seconds(1)); CHECK(offset == sf::seconds(1));

View File

@ -2,6 +2,7 @@
#include <type_traits> #include <type_traits>
static_assert(std::is_default_constructible_v<sf::OutputSoundFile>);
static_assert(!std::is_copy_constructible_v<sf::OutputSoundFile>); static_assert(!std::is_copy_constructible_v<sf::OutputSoundFile>);
static_assert(!std::is_copy_assignable_v<sf::OutputSoundFile>); static_assert(!std::is_copy_assignable_v<sf::OutputSoundFile>);
static_assert(std::is_nothrow_move_constructible_v<sf::OutputSoundFile>); static_assert(std::is_nothrow_move_constructible_v<sf::OutputSoundFile>);

View File

@ -26,7 +26,7 @@ TEST_CASE("[Audio] sf::Sound", runAudioDeviceTests())
STATIC_CHECK(std::has_virtual_destructor_v<sf::Sound>); STATIC_CHECK(std::has_virtual_destructor_v<sf::Sound>);
} }
const auto soundBuffer = sf::SoundBuffer::createFromFile("Audio/ding.flac").value(); const sf::SoundBuffer soundBuffer("Audio/ding.flac");
SECTION("Construction") SECTION("Construction")
{ {
@ -52,7 +52,7 @@ TEST_CASE("[Audio] sf::Sound", runAudioDeviceTests())
SECTION("Assignment") SECTION("Assignment")
{ {
const sf::SoundBuffer otherSoundBuffer = sf::SoundBuffer::createFromFile("Audio/ding.flac").value(); const sf::SoundBuffer otherSoundBuffer("Audio/ding.flac");
sf::Sound soundCopy(otherSoundBuffer); sf::Sound soundCopy(otherSoundBuffer);
soundCopy = sound; soundCopy = sound;
CHECK(&soundCopy.getBuffer() == &soundBuffer); CHECK(&soundCopy.getBuffer() == &soundBuffer);
@ -64,7 +64,7 @@ TEST_CASE("[Audio] sf::Sound", runAudioDeviceTests())
SECTION("Set/get buffer") SECTION("Set/get buffer")
{ {
const sf::SoundBuffer otherSoundBuffer = sf::SoundBuffer::createFromFile("Audio/ding.flac").value(); const sf::SoundBuffer otherSoundBuffer("Audio/ding.flac");
sf::Sound sound(soundBuffer); sf::Sound sound(soundBuffer);
sound.setBuffer(otherSoundBuffer); sound.setBuffer(otherSoundBuffer);
CHECK(&sound.getBuffer() == &otherSoundBuffer); CHECK(&sound.getBuffer() == &otherSoundBuffer);

View File

@ -23,6 +23,8 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
} }
SECTION("Construction") SECTION("Construction")
{
SECTION("Default constructor")
{ {
const sf::SoundBuffer soundBuffer; const sf::SoundBuffer soundBuffer;
CHECK(soundBuffer.getSamples() == nullptr); CHECK(soundBuffer.getSamples() == nullptr);
@ -32,9 +34,59 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
CHECK(soundBuffer.getDuration() == sf::Time::Zero); CHECK(soundBuffer.getDuration() == sf::Time::Zero);
} }
SECTION("File")
{
SECTION("Invalid filename")
{
CHECK_THROWS_AS(sf::SoundBuffer("does/not/exist.wav"), std::runtime_error);
}
SECTION("Valid file")
{
const sf::SoundBuffer soundBuffer("Audio/ding.flac");
CHECK(soundBuffer.getSamples() != nullptr);
CHECK(soundBuffer.getSampleCount() == 87798);
CHECK(soundBuffer.getSampleRate() == 44100);
CHECK(soundBuffer.getChannelCount() == 1);
CHECK(soundBuffer.getDuration() == sf::microseconds(1990884));
}
}
SECTION("Memory")
{
SECTION("Invalid memory")
{
constexpr std::array<std::byte, 5> memory{};
CHECK_THROWS_AS(sf::SoundBuffer(memory.data(), memory.size()), std::runtime_error);
}
SECTION("Valid memory")
{
const auto memory = loadIntoMemory("Audio/ding.flac");
const sf::SoundBuffer soundBuffer(memory.data(), memory.size());
CHECK(soundBuffer.getSamples() != nullptr);
CHECK(soundBuffer.getSampleCount() == 87798);
CHECK(soundBuffer.getSampleRate() == 44100);
CHECK(soundBuffer.getChannelCount() == 1);
CHECK(soundBuffer.getDuration() == sf::microseconds(1990884));
}
}
SECTION("Stream")
{
sf::FileInputStream stream("Audio/ding.flac");
const sf::SoundBuffer soundBuffer(stream);
CHECK(soundBuffer.getSamples() != nullptr);
CHECK(soundBuffer.getSampleCount() == 87798);
CHECK(soundBuffer.getSampleRate() == 44100);
CHECK(soundBuffer.getChannelCount() == 1);
CHECK(soundBuffer.getDuration() == sf::microseconds(1990884));
}
}
SECTION("Copy semantics") SECTION("Copy semantics")
{ {
const auto soundBuffer = sf::SoundBuffer::createFromFile("Audio/ding.flac").value(); const sf::SoundBuffer soundBuffer("Audio/ding.flac");
SECTION("Construction") SECTION("Construction")
{ {
@ -48,7 +100,7 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
SECTION("Assignment") SECTION("Assignment")
{ {
sf::SoundBuffer soundBufferCopy = sf::SoundBuffer::createFromFile("Audio/doodle_pop.ogg").value(); sf::SoundBuffer soundBufferCopy("Audio/doodle_pop.ogg");
soundBufferCopy = soundBuffer; soundBufferCopy = soundBuffer;
CHECK(soundBufferCopy.getSamples() != nullptr); CHECK(soundBufferCopy.getSamples() != nullptr);
CHECK(soundBufferCopy.getSampleCount() == 87798); CHECK(soundBufferCopy.getSampleCount() == 87798);
@ -123,65 +175,16 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
} }
} }
SECTION("createFromFile()")
{
SECTION("Invalid filename")
{
CHECK(!sf::SoundBuffer::createFromFile("does/not/exist.wav"));
}
SECTION("Valid file")
{
const auto soundBuffer = sf::SoundBuffer::createFromFile("Audio/ding.flac").value();
CHECK(soundBuffer.getSamples() != nullptr);
CHECK(soundBuffer.getSampleCount() == 87798);
CHECK(soundBuffer.getSampleRate() == 44100);
CHECK(soundBuffer.getChannelCount() == 1);
CHECK(soundBuffer.getDuration() == sf::microseconds(1990884));
}
}
SECTION("createFromMemory()")
{
SECTION("Invalid memory")
{
constexpr std::array<std::byte, 5> memory{};
CHECK(!sf::SoundBuffer::createFromMemory(memory.data(), memory.size()));
}
SECTION("Valid memory")
{
const auto memory = loadIntoMemory("Audio/ding.flac");
const auto soundBuffer = sf::SoundBuffer::createFromMemory(memory.data(), memory.size()).value();
CHECK(soundBuffer.getSamples() != nullptr);
CHECK(soundBuffer.getSampleCount() == 87798);
CHECK(soundBuffer.getSampleRate() == 44100);
CHECK(soundBuffer.getChannelCount() == 1);
CHECK(soundBuffer.getDuration() == sf::microseconds(1990884));
}
}
SECTION("createFromStream()")
{
auto stream = sf::FileInputStream::create("Audio/ding.flac").value();
const auto soundBuffer = sf::SoundBuffer::createFromStream(stream).value();
CHECK(soundBuffer.getSamples() != nullptr);
CHECK(soundBuffer.getSampleCount() == 87798);
CHECK(soundBuffer.getSampleRate() == 44100);
CHECK(soundBuffer.getChannelCount() == 1);
CHECK(soundBuffer.getDuration() == sf::microseconds(1990884));
}
SECTION("saveToFile()") SECTION("saveToFile()")
{ {
const auto filename = std::filesystem::temp_directory_path() / "ding.flac"; const auto filename = std::filesystem::temp_directory_path() / "ding.flac";
{ {
const auto soundBuffer = sf::SoundBuffer::createFromFile("Audio/ding.flac").value(); const sf::SoundBuffer soundBuffer("Audio/ding.flac");
REQUIRE(soundBuffer.saveToFile(filename)); REQUIRE(soundBuffer.saveToFile(filename));
} }
const auto soundBuffer = sf::SoundBuffer::createFromFile(filename).value(); const sf::SoundBuffer soundBuffer(filename);
CHECK(soundBuffer.getSamples() != nullptr); CHECK(soundBuffer.getSamples() != nullptr);
CHECK(soundBuffer.getSampleCount() == 87798); CHECK(soundBuffer.getSampleCount() == 87798);
CHECK(soundBuffer.getSampleRate() == 44100); CHECK(soundBuffer.getSampleRate() == 44100);

View File

@ -11,7 +11,6 @@
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <filesystem> #include <filesystem>
#include <optional>
#include <type_traits> #include <type_traits>
#include <cstdint> #include <cstdint>
@ -110,30 +109,29 @@ TEST_CASE("[Audio] sf::SoundFileFactory")
SECTION("createReaderFromStream()") SECTION("createReaderFromStream()")
{ {
std::optional<sf::FileInputStream> stream; sf::FileInputStream stream;
SECTION("flac") SECTION("flac")
{ {
stream = sf::FileInputStream::create("Audio/ding.flac"); REQUIRE(stream.open("Audio/ding.flac"));
} }
SECTION("mp3") SECTION("mp3")
{ {
stream = sf::FileInputStream::create("Audio/ding.mp3"); REQUIRE(stream.open("Audio/ding.mp3"));
} }
SECTION("ogg") SECTION("ogg")
{ {
stream = sf::FileInputStream::create("Audio/doodle_pop.ogg"); REQUIRE(stream.open("Audio/doodle_pop.ogg"));
} }
SECTION("wav") SECTION("wav")
{ {
stream = sf::FileInputStream::create("Audio/killdeer.wav"); REQUIRE(stream.open("Audio/killdeer.wav"));
} }
REQUIRE(stream); CHECK(sf::SoundFileFactory::createReaderFromStream(stream));
CHECK(sf::SoundFileFactory::createReaderFromStream(*stream));
} }
SECTION("createWriterFromFilename()") SECTION("createWriterFromFilename()")

View File

@ -46,7 +46,7 @@ TEST_CASE("[Graphics] sf::Drawable", runDisplayTests())
SECTION("draw()") SECTION("draw()")
{ {
const DrawableTest drawableTest; const DrawableTest drawableTest;
auto renderTexture = sf::RenderTexture::create({32, 32}).value(); sf::RenderTexture renderTexture({32, 32});
CHECK(drawableTest.callCount() == 0); CHECK(drawableTest.callCount() == 0);
renderTexture.draw(drawableTest); renderTexture.draw(drawableTest);
CHECK(drawableTest.callCount() == 1); CHECK(drawableTest.callCount() == 1);

View File

@ -22,6 +22,8 @@ TEST_CASE("[Graphics] sf::Font", runDisplayTests())
} }
SECTION("Construction") SECTION("Construction")
{
SECTION("Default constructor")
{ {
const sf::Font font; const sf::Font font;
CHECK(font.getInfo().family.empty()); CHECK(font.getInfo().family.empty());
@ -32,6 +34,105 @@ TEST_CASE("[Graphics] sf::Font", runDisplayTests())
CHECK(font.isSmooth()); CHECK(font.isSmooth());
} }
SECTION("File")
{
SECTION("Invalid filename")
{
CHECK_THROWS_AS(sf::Font("does/not/exist.ttf"), std::runtime_error);
}
SECTION("Successful load")
{
const sf::Font font("Graphics/tuffy.ttf");
CHECK(font.getInfo().family == "Tuffy");
const auto& glyph = font.getGlyph(0x45, 16, false);
CHECK(glyph.advance == 9);
CHECK(glyph.lsbDelta == 9);
CHECK(glyph.rsbDelta == 16);
CHECK(glyph.bounds == sf::FloatRect({0, -12}, {8, 12}));
CHECK(glyph.textureRect == sf::IntRect({2, 5}, {8, 12}));
CHECK(font.hasGlyph(0x41));
CHECK(font.hasGlyph(0xC0));
CHECK(font.getKerning(0x41, 0x42, 12) == -1);
CHECK(font.getKerning(0x43, 0x44, 24, true) == 0);
CHECK(font.getLineSpacing(24) == 30);
CHECK(font.getUnderlinePosition(36) == Approx(2.20312f));
CHECK(font.getUnderlineThickness(48) == Approx(1.17188f));
const auto& texture = font.getTexture(10);
CHECK(texture.getSize() == sf::Vector2u(128, 128));
CHECK(texture.isSmooth());
CHECK(!texture.isSrgb());
CHECK(!texture.isRepeated());
CHECK(texture.getNativeHandle() != 0);
CHECK(font.isSmooth());
}
}
SECTION("Memory")
{
SECTION("Invalid data and size")
{
CHECK_THROWS_AS(sf::Font(nullptr, 1), std::runtime_error);
const std::byte testByte{0xCD};
CHECK_THROWS_AS(sf::Font(&testByte, 0), std::runtime_error);
}
SECTION("Successful load")
{
const auto memory = loadIntoMemory("Graphics/tuffy.ttf");
const sf::Font font(memory.data(), memory.size());
CHECK(font.getInfo().family == "Tuffy");
const auto& glyph = font.getGlyph(0x45, 16, false);
CHECK(glyph.advance == 9);
CHECK(glyph.lsbDelta == 9);
CHECK(glyph.rsbDelta == 16);
CHECK(glyph.bounds == sf::FloatRect({0, -12}, {8, 12}));
CHECK(glyph.textureRect == sf::IntRect({2, 5}, {8, 12}));
CHECK(font.hasGlyph(0x41));
CHECK(font.hasGlyph(0xC0));
CHECK(font.getKerning(0x41, 0x42, 12) == -1);
CHECK(font.getKerning(0x43, 0x44, 24, true) == 0);
CHECK(font.getLineSpacing(24) == 30);
CHECK(font.getUnderlinePosition(36) == Approx(2.20312f));
CHECK(font.getUnderlineThickness(48) == Approx(1.17188f));
const auto& texture = font.getTexture(10);
CHECK(texture.getSize() == sf::Vector2u(128, 128));
CHECK(texture.isSmooth());
CHECK(!texture.isSrgb());
CHECK(!texture.isRepeated());
CHECK(texture.getNativeHandle() != 0);
CHECK(font.isSmooth());
}
}
SECTION("Stream")
{
sf::FileInputStream stream("Graphics/tuffy.ttf");
const sf::Font font(stream);
CHECK(font.getInfo().family == "Tuffy");
const auto& glyph = font.getGlyph(0x45, 16, false);
CHECK(glyph.advance == 9);
CHECK(glyph.lsbDelta == 9);
CHECK(glyph.rsbDelta == 16);
CHECK(glyph.bounds == sf::FloatRect({0, -12}, {8, 12}));
CHECK(glyph.textureRect == sf::IntRect({2, 5}, {8, 12}));
CHECK(font.hasGlyph(0x41));
CHECK(font.hasGlyph(0xC0));
CHECK(font.getKerning(0x41, 0x42, 12) == -1);
CHECK(font.getKerning(0x43, 0x44, 24, true) == 0);
CHECK(font.getLineSpacing(24) == 30);
CHECK(font.getUnderlinePosition(36) == Approx(2.20312f));
CHECK(font.getUnderlineThickness(48) == Approx(1.17188f));
const auto& texture = font.getTexture(10);
CHECK(texture.getSize() == sf::Vector2u(128, 128));
CHECK(texture.isSmooth());
CHECK(!texture.isSrgb());
CHECK(!texture.isRepeated());
CHECK(texture.getNativeHandle() != 0);
CHECK(font.isSmooth());
}
}
SECTION("openFromFile()") SECTION("openFromFile()")
{ {
sf::Font font; sf::Font font;
@ -145,107 +246,9 @@ TEST_CASE("[Graphics] sf::Font", runDisplayTests())
} }
} }
SECTION("createFromFile()")
{
SECTION("Invalid filename")
{
CHECK(!sf::Font::createFromFile("does/not/exist.ttf"));
}
SECTION("Valid file")
{
const auto font = sf::Font::createFromFile("Graphics/tuffy.ttf").value();
CHECK(font.getInfo().family == "Tuffy");
const auto& glyph = font.getGlyph(0x45, 16, false);
CHECK(glyph.advance == 9);
CHECK(glyph.lsbDelta == 9);
CHECK(glyph.rsbDelta == 16);
CHECK(glyph.bounds == sf::FloatRect({0, -12}, {8, 12}));
CHECK(glyph.textureRect == sf::IntRect({2, 5}, {8, 12}));
CHECK(font.hasGlyph(0x41));
CHECK(font.hasGlyph(0xC0));
CHECK(font.getKerning(0x41, 0x42, 12) == -1);
CHECK(font.getKerning(0x43, 0x44, 24, true) == 0);
CHECK(font.getLineSpacing(24) == 30);
CHECK(font.getUnderlinePosition(36) == Approx(2.20312f));
CHECK(font.getUnderlineThickness(48) == Approx(1.17188f));
const auto& texture = font.getTexture(10);
CHECK(texture.getSize() == sf::Vector2u(128, 128));
CHECK(texture.isSmooth());
CHECK(!texture.isSrgb());
CHECK(!texture.isRepeated());
CHECK(texture.getNativeHandle() != 0);
CHECK(font.isSmooth());
}
}
SECTION("createFromMemory()")
{
SECTION("Invalid data and size")
{
CHECK(!sf::Font::createFromMemory(nullptr, 1));
const std::byte testByte{0xCD};
CHECK(!sf::Font::createFromMemory(&testByte, 0));
}
SECTION("Valid data")
{
const auto memory = loadIntoMemory("Graphics/tuffy.ttf");
const auto font = sf::Font::createFromMemory(memory.data(), memory.size()).value();
CHECK(font.getInfo().family == "Tuffy");
const auto& glyph = font.getGlyph(0x45, 16, false);
CHECK(glyph.advance == 9);
CHECK(glyph.lsbDelta == 9);
CHECK(glyph.rsbDelta == 16);
CHECK(glyph.bounds == sf::FloatRect({0, -12}, {8, 12}));
CHECK(glyph.textureRect == sf::IntRect({2, 5}, {8, 12}));
CHECK(font.hasGlyph(0x41));
CHECK(font.hasGlyph(0xC0));
CHECK(font.getKerning(0x41, 0x42, 12) == -1);
CHECK(font.getKerning(0x43, 0x44, 24, true) == 0);
CHECK(font.getLineSpacing(24) == 30);
CHECK(font.getUnderlinePosition(36) == Approx(2.20312f));
CHECK(font.getUnderlineThickness(48) == Approx(1.17188f));
const auto& texture = font.getTexture(10);
CHECK(texture.getSize() == sf::Vector2u(128, 128));
CHECK(texture.isSmooth());
CHECK(!texture.isSrgb());
CHECK(!texture.isRepeated());
CHECK(texture.getNativeHandle() != 0);
CHECK(font.isSmooth());
}
}
SECTION("createFromStream()")
{
auto stream = sf::FileInputStream::create("Graphics/tuffy.ttf").value();
const auto font = sf::Font::createFromStream(stream).value();
CHECK(font.getInfo().family == "Tuffy");
const auto& glyph = font.getGlyph(0x45, 16, false);
CHECK(glyph.advance == 9);
CHECK(glyph.lsbDelta == 9);
CHECK(glyph.rsbDelta == 16);
CHECK(glyph.bounds == sf::FloatRect({0, -12}, {8, 12}));
CHECK(glyph.textureRect == sf::IntRect({2, 5}, {8, 12}));
CHECK(font.hasGlyph(0x41));
CHECK(font.hasGlyph(0xC0));
CHECK(font.getKerning(0x41, 0x42, 12) == -1);
CHECK(font.getKerning(0x43, 0x44, 24, true) == 0);
CHECK(font.getLineSpacing(24) == 30);
CHECK(font.getUnderlinePosition(36) == Approx(2.20312f));
CHECK(font.getUnderlineThickness(48) == Approx(1.17188f));
const auto& texture = font.getTexture(10);
CHECK(texture.getSize() == sf::Vector2u(128, 128));
CHECK(texture.isSmooth());
CHECK(!texture.isSrgb());
CHECK(!texture.isRepeated());
CHECK(texture.getNativeHandle() != 0);
CHECK(font.isSmooth());
}
SECTION("Set/get smooth") SECTION("Set/get smooth")
{ {
auto font = sf::Font::createFromFile("Graphics/tuffy.ttf").value(); sf::Font font("Graphics/tuffy.ttf");
font.setSmooth(false); font.setSmooth(false);
CHECK(!font.isSmooth()); CHECK(!font.isSmooth());
} }

View File

@ -19,6 +19,8 @@ TEST_CASE("[Graphics] sf::Image")
STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::Image>); STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::Image>);
} }
SECTION("Construction")
{
SECTION("Default constructor") SECTION("Default constructor")
{ {
const sf::Image image; const sf::Image image;
@ -26,8 +28,114 @@ TEST_CASE("[Graphics] sf::Image")
CHECK(image.getPixelsPtr() == nullptr); CHECK(image.getPixelsPtr() == nullptr);
} }
SECTION("Construction") SECTION("File constructor")
{ {
SECTION("Invalid file")
{
CHECK_THROWS_AS(sf::Image("."), std::runtime_error);
CHECK_THROWS_AS(sf::Image("this/does/not/exist.jpg"), std::runtime_error);
}
SECTION("Successful load")
{
SECTION("bmp")
{
const sf::Image image("Graphics/sfml-logo-big.bmp");
CHECK(image.getPixel({0, 0}) == sf::Color::White);
CHECK(image.getPixel({200, 150}) == sf::Color(144, 208, 62));
CHECK(image.getSize() == sf::Vector2u(1001, 304));
CHECK(image.getPixelsPtr() != nullptr);
}
SECTION("png")
{
const sf::Image image("Graphics/sfml-logo-big.png");
CHECK(image.getPixel({0, 0}) == sf::Color(255, 255, 255, 0));
CHECK(image.getPixel({200, 150}) == sf::Color(144, 208, 62));
CHECK(image.getSize() == sf::Vector2u(1001, 304));
CHECK(image.getPixelsPtr() != nullptr);
}
SECTION("jpg")
{
const sf::Image image("Graphics/sfml-logo-big.jpg");
CHECK(image.getPixel({0, 0}) == sf::Color::White);
CHECK(image.getPixel({200, 150}) == sf::Color(144, 208, 62));
CHECK(image.getSize() == sf::Vector2u(1001, 304));
CHECK(image.getPixelsPtr() != nullptr);
}
SECTION("gif")
{
const sf::Image image("Graphics/sfml-logo-big.gif");
CHECK(image.getPixel({0, 0}) == sf::Color::White);
CHECK(image.getPixel({200, 150}) == sf::Color(146, 210, 62));
CHECK(image.getSize() == sf::Vector2u(1001, 304));
CHECK(image.getPixelsPtr() != nullptr);
}
SECTION("psd")
{
const sf::Image image("Graphics/sfml-logo-big.psd");
CHECK(image.getPixel({0, 0}) == sf::Color::White);
CHECK(image.getPixel({200, 150}) == sf::Color(144, 208, 62));
CHECK(image.getSize() == sf::Vector2u(1001, 304));
CHECK(image.getPixelsPtr() != nullptr);
}
}
}
SECTION("Memory constructor")
{
SECTION("Invalid pointer")
{
CHECK_THROWS_AS(sf::Image(nullptr, 1), std::runtime_error);
}
SECTION("Invalid size")
{
const std::byte testByte{0xAB};
CHECK_THROWS_AS(sf::Image(&testByte, 0), std::runtime_error);
}
SECTION("Failed load")
{
std::vector<std::uint8_t> memory;
SECTION("Empty")
{
memory.clear();
}
SECTION("Junk data")
{
memory = {1, 2, 3, 4};
}
CHECK_THROWS_AS(sf::Image(memory.data(), memory.size()), std::runtime_error);
}
SECTION("Successful load")
{
const auto memory = sf::Image({24, 24}, sf::Color::Green).saveToMemory("png").value();
const sf::Image image(memory.data(), memory.size());
CHECK(image.getSize() == sf::Vector2u(24, 24));
CHECK(image.getPixelsPtr() != nullptr);
CHECK(image.getPixel({0, 0}) == sf::Color::Green);
CHECK(image.getPixel({23, 23}) == sf::Color::Green);
}
}
SECTION("Stream constructor")
{
sf::FileInputStream stream("Graphics/sfml-logo-big.png");
const sf::Image image(stream);
CHECK(image.getSize() == sf::Vector2u(1001, 304));
CHECK(image.getPixelsPtr() != nullptr);
CHECK(image.getPixel({0, 0}) == sf::Color(255, 255, 255, 0));
CHECK(image.getPixel({200, 150}) == sf::Color(144, 208, 62));
}
SECTION("Vector2 constructor") SECTION("Vector2 constructor")
{ {
const sf::Image image(sf::Vector2u(10, 10)); const sf::Image image(sf::Vector2u(10, 10));
@ -272,114 +380,6 @@ TEST_CASE("[Graphics] sf::Image")
} }
} }
SECTION("createFromFile()")
{
SECTION("Invalid file")
{
CHECK(!sf::Image::createFromFile("."));
CHECK(!sf::Image::createFromFile("this/does/not/exist.jpg"));
}
SECTION("Successful load")
{
std::optional<sf::Image> image;
SECTION("bmp")
{
image = sf::Image::createFromFile("Graphics/sfml-logo-big.bmp").value();
REQUIRE(image.has_value());
CHECK(image->getPixel({0, 0}) == sf::Color::White);
CHECK(image->getPixel({200, 150}) == sf::Color(144, 208, 62));
}
SECTION("png")
{
image = sf::Image::createFromFile("Graphics/sfml-logo-big.png").value();
REQUIRE(image.has_value());
CHECK(image->getPixel({0, 0}) == sf::Color(255, 255, 255, 0));
CHECK(image->getPixel({200, 150}) == sf::Color(144, 208, 62));
}
SECTION("jpg")
{
image = sf::Image::createFromFile("Graphics/sfml-logo-big.jpg").value();
REQUIRE(image.has_value());
CHECK(image->getPixel({0, 0}) == sf::Color::White);
CHECK(image->getPixel({200, 150}) == sf::Color(144, 208, 62));
}
SECTION("gif")
{
image = sf::Image::createFromFile("Graphics/sfml-logo-big.gif").value();
REQUIRE(image.has_value());
CHECK(image->getPixel({0, 0}) == sf::Color::White);
CHECK(image->getPixel({200, 150}) == sf::Color(146, 210, 62));
}
SECTION("psd")
{
image = sf::Image::createFromFile("Graphics/sfml-logo-big.psd").value();
REQUIRE(image.has_value());
CHECK(image->getPixel({0, 0}) == sf::Color::White);
CHECK(image->getPixel({200, 150}) == sf::Color(144, 208, 62));
}
CHECK(image->getSize() == sf::Vector2u(1001, 304));
CHECK(image->getPixelsPtr() != nullptr);
}
}
SECTION("createFromMemory()")
{
SECTION("Invalid pointer")
{
CHECK(!sf::Image::createFromMemory(nullptr, 1));
}
SECTION("Invalid size")
{
const std::byte testByte{0xAB};
CHECK(!sf::Image::createFromMemory(&testByte, 0));
}
SECTION("Failed load")
{
std::vector<std::uint8_t> memory;
SECTION("Empty")
{
memory.clear();
}
SECTION("Junk data")
{
memory = {1, 2, 3, 4};
}
CHECK(!sf::Image::createFromMemory(memory.data(), memory.size()));
}
SECTION("Successful load")
{
const auto memory = sf::Image({24, 24}, sf::Color::Green).saveToMemory("png").value();
const auto image = sf::Image::createFromMemory(memory.data(), memory.size()).value();
CHECK(image.getSize() == sf::Vector2u(24, 24));
CHECK(image.getPixelsPtr() != nullptr);
CHECK(image.getPixel({0, 0}) == sf::Color::Green);
CHECK(image.getPixel({23, 23}) == sf::Color::Green);
}
}
SECTION("createFromStream()")
{
auto stream = sf::FileInputStream::create("Graphics/sfml-logo-big.png").value();
const auto image = sf::Image::createFromStream(stream).value();
CHECK(image.getSize() == sf::Vector2u(1001, 304));
CHECK(image.getPixelsPtr() != nullptr);
CHECK(image.getPixel({0, 0}) == sf::Color(255, 255, 255, 0));
CHECK(image.getPixel({200, 150}) == sf::Color(144, 208, 62));
}
SECTION("saveToFile()") SECTION("saveToFile()")
{ {
SECTION("Invalid size") SECTION("Invalid size")
@ -426,7 +426,7 @@ TEST_CASE("[Graphics] sf::Image")
// 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 auto loadedImage = sf::Image::createFromFile(filename).value(); const sf::Image loadedImage(filename);
CHECK(loadedImage.getSize() == sf::Vector2u(256, 256)); CHECK(loadedImage.getSize() == sf::Vector2u(256, 256));
CHECK(loadedImage.getPixelsPtr() != nullptr); CHECK(loadedImage.getPixelsPtr() != nullptr);

View File

@ -12,9 +12,7 @@ TEST_CASE("[Graphics] Render Tests", runDisplayTests())
{ {
SECTION("Stencil Tests") SECTION("Stencil Tests")
{ {
auto renderTexture = sf::RenderTexture::create({100, 100}, sf::ContextSettings{0 /* depthBits */, 8 /* stencilBits */}) sf::RenderTexture renderTexture({100, 100}, sf::ContextSettings{0 /* depthBits */, 8 /* stencilBits */});
.value();
renderTexture.clear(sf::Color::Red, 127); renderTexture.clear(sf::Color::Red, 127);
sf::RectangleShape shape1({100, 100}); sf::RectangleShape shape1({100, 100});

View File

@ -16,6 +16,8 @@ TEST_CASE("[Graphics] sf::RenderTexture", runDisplayTests())
} }
SECTION("Construction") SECTION("Construction")
{
SECTION("Default constructor")
{ {
const sf::RenderTexture renderTexture; const sf::RenderTexture renderTexture;
CHECK(!renderTexture.isSmooth()); CHECK(!renderTexture.isSmooth());
@ -23,14 +25,37 @@ TEST_CASE("[Graphics] sf::RenderTexture", runDisplayTests())
CHECK(renderTexture.getSize() == sf::Vector2u(0, 0)); CHECK(renderTexture.getSize() == sf::Vector2u(0, 0));
} }
SECTION("create()") SECTION("2 parameter constructor")
{ {
CHECK(!sf::RenderTexture::create({1'000'000, 1'000'000})); CHECK_THROWS_AS(sf::RenderTexture({1'000'000, 1'000'000}), std::runtime_error);
CHECK(sf::RenderTexture::create({100, 100}, sf::ContextSettings{8 /* depthBits */, 0 /* stencilBits */})); CHECK_NOTHROW(sf::RenderTexture({100, 100}, sf::ContextSettings{8 /* depthBits */, 0 /* stencilBits */}));
CHECK(sf::RenderTexture::create({100, 100}, sf::ContextSettings{0 /* depthBits */, 8 /* stencilBits */})); CHECK_NOTHROW(sf::RenderTexture({100, 100}, sf::ContextSettings{0 /* depthBits */, 8 /* stencilBits */}));
const auto renderTexture = sf::RenderTexture::create({360, 480}).value(); const sf::RenderTexture renderTexture({360, 480});
CHECK(renderTexture.getSize() == sf::Vector2u(360, 480));
CHECK(!renderTexture.isSmooth());
CHECK(!renderTexture.isRepeated());
CHECK(!renderTexture.isSrgb());
const auto& texture = renderTexture.getTexture();
CHECK(texture.getSize() == sf::Vector2u(360, 480));
CHECK(!texture.isSmooth());
CHECK(!texture.isSrgb());
CHECK(!texture.isRepeated());
CHECK(texture.getNativeHandle() != 0);
}
}
SECTION("resize()")
{
sf::RenderTexture renderTexture;
CHECK(!renderTexture.resize({1'000'000, 1'000'000}));
CHECK(renderTexture.resize({100, 100}, sf::ContextSettings{8 /* depthBits */, 0 /* stencilBits */}));
CHECK(renderTexture.resize({100, 100}, sf::ContextSettings{0 /* depthBits */, 8 /* stencilBits */}));
REQUIRE(renderTexture.resize({360, 480}));
CHECK(renderTexture.getSize() == sf::Vector2u(360, 480)); CHECK(renderTexture.getSize() == sf::Vector2u(360, 480));
CHECK(!renderTexture.isSmooth()); CHECK(!renderTexture.isSmooth());
CHECK(!renderTexture.isRepeated()); CHECK(!renderTexture.isRepeated());
@ -66,27 +91,27 @@ TEST_CASE("[Graphics] sf::RenderTexture", runDisplayTests())
SECTION("Set/get smooth") SECTION("Set/get smooth")
{ {
auto renderTexture = sf::RenderTexture::create({64, 64}).value(); sf::RenderTexture renderTexture({64, 64});
renderTexture.setSmooth(true); renderTexture.setSmooth(true);
CHECK(renderTexture.isSmooth()); CHECK(renderTexture.isSmooth());
} }
SECTION("Set/get repeated") SECTION("Set/get repeated")
{ {
auto renderTexture = sf::RenderTexture::create({64, 64}).value(); sf::RenderTexture renderTexture({64, 64});
renderTexture.setRepeated(true); renderTexture.setRepeated(true);
CHECK(renderTexture.isRepeated()); CHECK(renderTexture.isRepeated());
} }
SECTION("generateMipmap()") SECTION("generateMipmap()")
{ {
auto renderTexture = sf::RenderTexture::create({64, 64}).value(); sf::RenderTexture renderTexture({64, 64});
CHECK(renderTexture.generateMipmap()); CHECK(renderTexture.generateMipmap());
} }
SECTION("setActive()") SECTION("setActive()")
{ {
auto renderTexture = sf::RenderTexture::create({64, 64}).value(); sf::RenderTexture renderTexture({64, 64});
CHECK(renderTexture.setActive()); CHECK(renderTexture.setActive());
CHECK(renderTexture.setActive(false)); CHECK(renderTexture.setActive(false));
CHECK(renderTexture.setActive(true)); CHECK(renderTexture.setActive(true));
@ -94,7 +119,7 @@ TEST_CASE("[Graphics] sf::RenderTexture", runDisplayTests())
SECTION("getTexture()") SECTION("getTexture()")
{ {
const auto renderTexture = sf::RenderTexture::create({64, 64}).value(); const sf::RenderTexture renderTexture({64, 64});
CHECK(renderTexture.getTexture().getSize() == sf::Vector2u(64, 64)); CHECK(renderTexture.getTexture().getSize() == sf::Vector2u(64, 64));
} }
} }

View File

@ -75,7 +75,7 @@ TEST_CASE("[Graphics] sf::RenderWindow", runDisplayTests())
sf::ContextSettings{}); sf::ContextSettings{});
REQUIRE(window.getSize() == sf::Vector2u(256, 256)); REQUIRE(window.getSize() == sf::Vector2u(256, 256));
auto texture = sf::Texture::create(window.getSize()).value(); sf::Texture texture(window.getSize());
window.clear(sf::Color::Red); window.clear(sf::Color::Red);
texture.update(window); texture.update(window);

View File

@ -145,6 +145,18 @@ TEST_CASE("[Graphics] sf::Shader (Dummy Implementation)", skipShaderDummyTests()
CHECK_FALSE(sf::Shader::isGeometryAvailable()); CHECK_FALSE(sf::Shader::isGeometryAvailable());
} }
SECTION("Construct from memory")
{
CHECK_THROWS_AS(sf::Shader(std::string_view(vertexSource), sf::Shader::Type::Vertex), std::runtime_error);
CHECK_THROWS_AS(sf::Shader(std::string_view(geometrySource), sf::Shader::Type::Geometry), std::runtime_error);
CHECK_THROWS_AS(sf::Shader(std::string_view(fragmentSource), sf::Shader::Type::Fragment), std::runtime_error);
CHECK_THROWS_AS(sf::Shader(std::string_view(vertexSource), std::string_view(fragmentSource)), std::runtime_error);
CHECK_THROWS_AS(sf::Shader(std::string_view(vertexSource),
std::string_view(geometrySource),
std::string_view(fragmentSource)),
std::runtime_error);
}
SECTION("loadFromMemory()") SECTION("loadFromMemory()")
{ {
sf::Shader shader; sf::Shader shader;
@ -154,15 +166,6 @@ TEST_CASE("[Graphics] sf::Shader (Dummy Implementation)", skipShaderDummyTests()
CHECK_FALSE(shader.loadFromMemory(vertexSource, fragmentSource)); CHECK_FALSE(shader.loadFromMemory(vertexSource, fragmentSource));
CHECK_FALSE(shader.loadFromMemory(vertexSource, geometrySource, fragmentSource)); CHECK_FALSE(shader.loadFromMemory(vertexSource, geometrySource, fragmentSource));
} }
SECTION("createFromMemory()")
{
CHECK_FALSE(sf::Shader::createFromMemory(vertexSource, sf::Shader::Type::Vertex).has_value());
CHECK_FALSE(sf::Shader::createFromMemory(geometrySource, sf::Shader::Type::Geometry).has_value());
CHECK_FALSE(sf::Shader::createFromMemory(fragmentSource, sf::Shader::Type::Fragment).has_value());
CHECK_FALSE(sf::Shader::createFromMemory(vertexSource, fragmentSource).has_value());
CHECK_FALSE(sf::Shader::createFromMemory(vertexSource, geometrySource, fragmentSource).has_value());
}
} }
TEST_CASE("[Graphics] sf::Shader", skipShaderFullTests()) TEST_CASE("[Graphics] sf::Shader", skipShaderFullTests())
@ -176,11 +179,184 @@ TEST_CASE("[Graphics] sf::Shader", skipShaderFullTests())
} }
SECTION("Construction") SECTION("Construction")
{
SECTION("Default constructor")
{ {
const sf::Shader shader; const sf::Shader shader;
CHECK(shader.getNativeHandle() == 0); CHECK(shader.getNativeHandle() == 0);
} }
SECTION("File")
{
SECTION("One shader")
{
CHECK_THROWS_AS(sf::Shader(std::filesystem::path("does-not-exist.vert"), sf::Shader::Type::Vertex),
std::runtime_error);
if (sf::Shader::isAvailable())
{
CHECK(sf::Shader(std::filesystem::path("Graphics/shader.vert"), sf::Shader::Type::Vertex).getNativeHandle() !=
0);
CHECK(sf::Shader(std::filesystem::path("Graphics/shader.frag"), sf::Shader::Type::Fragment)
.getNativeHandle() != 0);
}
else
{
CHECK_THROWS_AS(sf::Shader(std::filesystem::path("Graphics/shader.vert"), sf::Shader::Type::Vertex),
std::runtime_error);
CHECK_THROWS_AS(sf::Shader(std::filesystem::path("Graphics/shader.frag"), sf::Shader::Type::Fragment),
std::runtime_error);
}
}
SECTION("Two shaders")
{
CHECK_THROWS_AS(sf::Shader(std::filesystem::path("does-not-exist.vert"),
std::filesystem::path("Graphics/shader.frag")),
std::runtime_error);
CHECK_THROWS_AS(sf::Shader(std::filesystem::path("Graphics/shader.vert"),
std::filesystem::path("does-not-exist.frag")),
std::runtime_error);
if (sf::Shader::isAvailable())
{
CHECK(sf::Shader(std::filesystem::path("Graphics/shader.vert"),
std::filesystem::path("Graphics/shader.frag"))
.getNativeHandle() != 0);
}
else
{
CHECK_THROWS_AS(sf::Shader(std::filesystem::path("Graphics/shader.vert"),
std::filesystem::path("Graphics/shader.frag")),
std::runtime_error);
}
}
SECTION("Three shaders")
{
CHECK_THROWS_AS(sf::Shader(std::filesystem::path("does-not-exist.vert"),
std::filesystem::path("Graphics/shader.geom"),
std::filesystem::path("Graphics/shader.frag")),
std::runtime_error);
CHECK_THROWS_AS(sf::Shader(std::filesystem::path("Graphics/shader.vert"),
std::filesystem::path("does-not-exist.geom"),
std::filesystem::path("Graphics/shader.frag")),
std::runtime_error);
CHECK_THROWS_AS(sf::Shader(std::filesystem::path("Graphics/shader.vert"),
std::filesystem::path("Graphics/shader.geom"),
std::filesystem::path("does-not-exist.frag")),
std::runtime_error);
if (sf::Shader::isGeometryAvailable())
{
CHECK(sf::Shader(std::filesystem::path("Graphics/shader.vert"),
std::filesystem::path("Graphics/shader.geom"),
std::filesystem::path("Graphics/shader.frag"))
.getNativeHandle() != 0);
}
else
{
CHECK_THROWS_AS(sf::Shader(std::filesystem::path("Graphics/shader.vert"),
std::filesystem::path("Graphics/shader.geom"),
std::filesystem::path("Graphics/shader.frag")),
std::runtime_error);
}
}
}
SECTION("Memory")
{
if (sf::Shader::isAvailable())
{
CHECK(sf::Shader(std::string_view(vertexSource), sf::Shader::Type::Vertex).getNativeHandle() != 0);
CHECK_THROWS_AS(sf::Shader(std::string_view(geometrySource), sf::Shader::Type::Geometry),
std::runtime_error);
CHECK(sf::Shader(std::string_view(fragmentSource), sf::Shader::Type::Fragment).getNativeHandle() != 0);
CHECK(sf::Shader(std::string_view(vertexSource), std::string_view(fragmentSource)).getNativeHandle() != 0);
}
else
{
CHECK_THROWS_AS(sf::Shader(std::string_view(vertexSource), sf::Shader::Type::Vertex), std::runtime_error);
CHECK_THROWS_AS(sf::Shader(std::string_view(geometrySource), sf::Shader::Type::Geometry),
std::runtime_error);
CHECK_THROWS_AS(sf::Shader(std::string_view(fragmentSource), sf::Shader::Type::Fragment),
std::runtime_error);
CHECK_THROWS_AS(sf::Shader(std::string_view(vertexSource), std::string_view(fragmentSource)),
std::runtime_error);
}
if (sf::Shader::isGeometryAvailable())
{
CHECK(sf::Shader(std::string_view(vertexSource), std::string_view(geometrySource), std::string_view(fragmentSource))
.getNativeHandle() != 0);
}
else
{
CHECK_THROWS_AS(sf::Shader(std::string_view(vertexSource),
std::string_view(geometrySource),
std::string_view(fragmentSource)),
std::runtime_error);
}
}
SECTION("Stream")
{
sf::FileInputStream vertexShaderStream("Graphics/shader.vert");
sf::FileInputStream fragmentShaderStream("Graphics/shader.frag");
sf::FileInputStream geometryShaderStream("Graphics/shader.geom");
sf::FileInputStream emptyStream("Graphics/invalid_shader.vert");
SECTION("One shader")
{
CHECK_THROWS_AS(sf::Shader(emptyStream, sf::Shader::Type::Vertex), std::runtime_error);
if (sf::Shader::isAvailable())
{
CHECK(sf::Shader(vertexShaderStream, sf::Shader::Type::Vertex).getNativeHandle() != 0);
CHECK(sf::Shader(fragmentShaderStream, sf::Shader::Type::Fragment).getNativeHandle() != 0);
}
else
{
CHECK_THROWS_AS(sf::Shader(vertexShaderStream, sf::Shader::Type::Vertex), std::runtime_error);
CHECK_THROWS_AS(sf::Shader(fragmentShaderStream, sf::Shader::Type::Fragment), std::runtime_error);
}
}
SECTION("Two shaders")
{
CHECK_THROWS_AS(sf::Shader(emptyStream, fragmentShaderStream), std::runtime_error);
CHECK_THROWS_AS(sf::Shader(vertexShaderStream, emptyStream), std::runtime_error);
if (sf::Shader::isAvailable())
{
CHECK(sf::Shader(vertexShaderStream, fragmentShaderStream).getNativeHandle() != 0);
}
else
{
CHECK_THROWS_AS(sf::Shader(vertexShaderStream, fragmentShaderStream), std::runtime_error);
}
}
SECTION("Three shaders")
{
CHECK_THROWS_AS(sf::Shader(emptyStream, geometryShaderStream, fragmentShaderStream), std::runtime_error);
CHECK_THROWS_AS(sf::Shader(vertexShaderStream, emptyStream, fragmentShaderStream), std::runtime_error);
CHECK_THROWS_AS(sf::Shader(vertexShaderStream, geometryShaderStream, emptyStream), std::runtime_error);
if (sf::Shader::isGeometryAvailable())
{
CHECK(sf::Shader(vertexShaderStream, geometryShaderStream, fragmentShaderStream).getNativeHandle() != 0);
}
else
{
CHECK_THROWS_AS(sf::Shader(vertexShaderStream, geometryShaderStream, fragmentShaderStream),
std::runtime_error);
}
}
}
}
SECTION("Move semantics") SECTION("Move semantics")
{ {
SECTION("Construction") SECTION("Construction")
@ -203,15 +379,15 @@ TEST_CASE("[Graphics] sf::Shader", skipShaderFullTests())
{ {
SECTION("Construction") SECTION("Construction")
{ {
sf::Shader movedShader = sf::Shader::createFromFile("Graphics/shader.vert", sf::Shader::Type::Vertex).value(); sf::Shader movedShader(std::filesystem::path("Graphics/shader.vert"), sf::Shader::Type::Vertex);
const sf::Shader shader = std::move(movedShader); const sf::Shader shader = std::move(movedShader);
CHECK(shader.getNativeHandle() != 0); CHECK(shader.getNativeHandle() != 0);
} }
SECTION("Assignment") SECTION("Assignment")
{ {
sf::Shader movedShader = sf::Shader::createFromFile("Graphics/shader.vert", sf::Shader::Type::Vertex).value(); sf::Shader movedShader(std::filesystem::path("Graphics/shader.vert"), sf::Shader::Type::Vertex);
sf::Shader shader = sf::Shader::createFromFile("Graphics/shader.frag", sf::Shader::Type::Fragment).value(); sf::Shader shader(std::filesystem::path("Graphics/shader.frag"), sf::Shader::Type::Fragment);
shader = std::move(movedShader); shader = std::move(movedShader);
CHECK(shader.getNativeHandle() != 0); CHECK(shader.getNativeHandle() != 0);
} }
@ -255,7 +431,7 @@ TEST_CASE("[Graphics] sf::Shader", skipShaderFullTests())
{ {
sf::Shader shader; sf::Shader shader;
CHECK(shader.loadFromMemory(vertexSource, sf::Shader::Type::Vertex) == sf::Shader::isAvailable()); CHECK(shader.loadFromMemory(vertexSource, sf::Shader::Type::Vertex) == sf::Shader::isAvailable());
CHECK_FALSE(shader.loadFromMemory(geometrySource, sf::Shader::Type::Geometry)); CHECK(!shader.loadFromMemory(geometrySource, sf::Shader::Type::Geometry));
CHECK(shader.loadFromMemory(fragmentSource, sf::Shader::Type::Fragment) == sf::Shader::isAvailable()); CHECK(shader.loadFromMemory(fragmentSource, sf::Shader::Type::Fragment) == sf::Shader::isAvailable());
CHECK(shader.loadFromMemory(vertexSource, fragmentSource) == sf::Shader::isAvailable()); CHECK(shader.loadFromMemory(vertexSource, fragmentSource) == sf::Shader::isAvailable());
CHECK(shader.loadFromMemory(vertexSource, geometrySource, fragmentSource) == sf::Shader::isGeometryAvailable()); CHECK(shader.loadFromMemory(vertexSource, geometrySource, fragmentSource) == sf::Shader::isGeometryAvailable());
@ -300,100 +476,4 @@ TEST_CASE("[Graphics] sf::Shader", skipShaderFullTests())
CHECK(static_cast<bool>(shader.getNativeHandle()) == sf::Shader::isGeometryAvailable()); CHECK(static_cast<bool>(shader.getNativeHandle()) == sf::Shader::isGeometryAvailable());
} }
} }
SECTION("createFromFile()")
{
SECTION("One shader")
{
CHECK(!sf::Shader::createFromFile("does-not-exist.vert", sf::Shader::Type::Vertex));
const auto vertexShader = sf::Shader::createFromFile("Graphics/shader.vert", sf::Shader::Type::Vertex);
CHECK(vertexShader.has_value() == sf::Shader::isAvailable());
if (vertexShader)
CHECK(static_cast<bool>(vertexShader->getNativeHandle()) == sf::Shader::isAvailable());
const auto fragmentShader = sf::Shader::createFromFile("Graphics/shader.frag", sf::Shader::Type::Fragment);
CHECK(fragmentShader.has_value() == sf::Shader::isAvailable());
if (fragmentShader)
CHECK(static_cast<bool>(fragmentShader->getNativeHandle()) == sf::Shader::isAvailable());
}
SECTION("Two shaders")
{
CHECK(!sf::Shader::createFromFile("does-not-exist.vert", "Graphics/shader.frag"));
CHECK(!sf::Shader::createFromFile("Graphics/shader.vert", "does-not-exist.frag"));
const auto shader = sf::Shader::createFromFile("Graphics/shader.vert", "Graphics/shader.frag");
CHECK(shader.has_value() == sf::Shader::isAvailable());
if (shader)
CHECK(static_cast<bool>(shader->getNativeHandle()) == sf::Shader::isAvailable());
}
SECTION("Three shaders")
{
CHECK(!sf::Shader::createFromFile("does-not-exist.vert", "Graphics/shader.geom", "Graphics/shader.frag"));
CHECK(!sf::Shader::createFromFile("Graphics/shader.vert", "does-not-exist.geom", "Graphics/shader.frag"));
CHECK(!sf::Shader::createFromFile("Graphics/shader.vert", "Graphics/shader.geom", "does-not-exist.frag"));
const auto shader = sf::Shader::createFromFile("Graphics/shader.vert",
"Graphics/shader.geom",
"Graphics/shader.frag");
CHECK(shader.has_value() == sf::Shader::isGeometryAvailable());
if (shader)
CHECK(static_cast<bool>(shader->getNativeHandle()) == sf::Shader::isGeometryAvailable());
}
}
SECTION("createFromMemory()")
{
CHECK(sf::Shader::createFromMemory(vertexSource, sf::Shader::Type::Vertex).has_value() ==
sf::Shader::isAvailable());
CHECK(!sf::Shader::createFromMemory(geometrySource, sf::Shader::Type::Geometry));
CHECK(sf::Shader::createFromMemory(fragmentSource, sf::Shader::Type::Fragment).has_value() ==
sf::Shader::isAvailable());
CHECK(sf::Shader::createFromMemory(vertexSource, fragmentSource).has_value() == sf::Shader::isAvailable());
const auto shader = sf::Shader::createFromMemory(vertexSource, geometrySource, fragmentSource);
CHECK(shader.has_value() == sf::Shader::isGeometryAvailable());
if (shader)
CHECK(static_cast<bool>(shader->getNativeHandle()) == sf::Shader::isAvailable());
}
SECTION("createFromStream()")
{
auto vertexShaderStream = sf::FileInputStream::create("Graphics/shader.vert").value();
auto fragmentShaderStream = sf::FileInputStream::create("Graphics/shader.frag").value();
auto geometryShaderStream = sf::FileInputStream::create("Graphics/shader.geom").value();
auto emptyStream = sf::FileInputStream::create("Graphics/invalid_shader.vert").value();
SECTION("One shader")
{
CHECK(!sf::Shader::createFromStream(emptyStream, sf::Shader::Type::Vertex));
CHECK(sf::Shader::createFromStream(vertexShaderStream, sf::Shader::Type::Vertex).has_value() ==
sf::Shader::isAvailable());
CHECK(sf::Shader::createFromStream(fragmentShaderStream, sf::Shader::Type::Fragment).has_value() ==
sf::Shader::isAvailable());
}
SECTION("Two shaders")
{
CHECK(!sf::Shader::createFromStream(emptyStream, fragmentShaderStream));
CHECK(!sf::Shader::createFromStream(vertexShaderStream, emptyStream));
CHECK(sf::Shader::createFromStream(vertexShaderStream, fragmentShaderStream).has_value() ==
sf::Shader::isAvailable());
}
SECTION("Three shaders")
{
CHECK(!sf::Shader::createFromStream(emptyStream, geometryShaderStream, fragmentShaderStream));
CHECK(!sf::Shader::createFromStream(vertexShaderStream, emptyStream, fragmentShaderStream));
CHECK(!sf::Shader::createFromStream(vertexShaderStream, geometryShaderStream, emptyStream));
const auto shader = sf::Shader::createFromStream(vertexShaderStream, geometryShaderStream, fragmentShaderStream);
CHECK(shader.has_value() == sf::Shader::isGeometryAvailable());
if (shader)
CHECK(static_cast<bool>(shader->getNativeHandle()) == sf::Shader::isGeometryAvailable());
}
}
} }

View File

@ -66,7 +66,7 @@ TEST_CASE("[Graphics] sf::Shape", runDisplayTests())
SECTION("Set/get texture") SECTION("Set/get texture")
{ {
const auto texture = sf::Texture::create({64, 64}).value(); const sf::Texture texture(sf::Vector2u(64, 64));
TriangleShape triangleShape({}); TriangleShape triangleShape({});
triangleShape.setTexture(&texture, true); triangleShape.setTexture(&texture, true);
CHECK(triangleShape.getTexture() == &texture); CHECK(triangleShape.getTexture() == &texture);

View File

@ -22,7 +22,7 @@ TEST_CASE("[Graphics] sf::Sprite", runDisplayTests())
STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::Sprite>); STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::Sprite>);
} }
const auto texture = sf::Texture::create({64, 64}).value(); const sf::Texture texture(sf::Vector2u(64, 64));
SECTION("Construction") SECTION("Construction")
{ {
@ -60,7 +60,7 @@ TEST_CASE("[Graphics] sf::Sprite", runDisplayTests())
SECTION("Set/get texture") SECTION("Set/get texture")
{ {
sf::Sprite sprite(texture); sf::Sprite sprite(texture);
const sf::Texture otherTexture = sf::Texture::create({64, 64}).value(); const sf::Texture otherTexture(sf::Vector2u(64, 64));
sprite.setTexture(otherTexture); sprite.setTexture(otherTexture);
CHECK(&sprite.getTexture() == &otherTexture); CHECK(&sprite.getTexture() == &otherTexture);
} }

View File

@ -21,7 +21,7 @@ TEST_CASE("[Graphics] sf::Text", runDisplayTests())
STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::Text>); STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::Text>);
} }
const auto font = sf::Font::createFromFile("Graphics/tuffy.ttf").value(); const sf::Font font("Graphics/tuffy.ttf");
SECTION("Construction") SECTION("Construction")
{ {
@ -87,7 +87,7 @@ TEST_CASE("[Graphics] sf::Text", runDisplayTests())
SECTION("Set/get font") SECTION("Set/get font")
{ {
sf::Text text(font); sf::Text text(font);
const auto otherFont = sf::Font::createFromFile("Graphics/tuffy.ttf").value(); const sf::Font otherFont("Graphics/tuffy.ttf");
text.setFont(otherFont); text.setFont(otherFont);
CHECK(&text.getFont() == &otherFont); CHECK(&text.getFont() == &otherFont);
} }

View File

@ -23,6 +23,8 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
} }
SECTION("Construction") SECTION("Construction")
{
SECTION("Default constructor")
{ {
const sf::Texture texture; const sf::Texture texture;
CHECK(texture.getSize() == sf::Vector2u()); CHECK(texture.getSize() == sf::Vector2u());
@ -32,6 +34,91 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
CHECK(texture.getNativeHandle() == 0); CHECK(texture.getNativeHandle() == 0);
} }
SECTION("Vector")
{
SECTION("At least one zero dimension")
{
CHECK_THROWS_AS(sf::Texture(sf::Vector2u()), std::runtime_error);
CHECK_THROWS_AS(sf::Texture(sf::Vector2u(0, 1)), std::runtime_error);
CHECK_THROWS_AS(sf::Texture(sf::Vector2u(1, 0)), std::runtime_error);
}
SECTION("Valid size")
{
const sf::Texture texture(sf::Vector2u(100, 100));
CHECK(texture.getSize() == sf::Vector2u(100, 100));
CHECK(texture.getNativeHandle() != 0);
}
SECTION("Too large")
{
CHECK_THROWS_AS(sf::Texture(sf::Vector2u(100'000, 100'000)), std::runtime_error);
CHECK_THROWS_AS(sf::Texture(sf::Vector2u(1'000'000, 1'000'000)), std::runtime_error);
}
}
SECTION("File")
{
const sf::Texture texture("Graphics/sfml-logo-big.png");
CHECK(texture.getSize() == sf::Vector2u(1001, 304));
CHECK(!texture.isSmooth());
CHECK(!texture.isSrgb());
CHECK(!texture.isRepeated());
CHECK(texture.getNativeHandle() != 0);
}
SECTION("Memory")
{
const auto memory = loadIntoMemory("Graphics/sfml-logo-big.png");
const sf::Texture texture(memory.data(), memory.size());
CHECK(texture.getSize() == sf::Vector2u(1001, 304));
CHECK(!texture.isSmooth());
CHECK(!texture.isSrgb());
CHECK(!texture.isRepeated());
CHECK(texture.getNativeHandle() != 0);
}
SECTION("Stream")
{
sf::FileInputStream stream("Graphics/sfml-logo-big.png");
const sf::Texture texture(stream);
CHECK(texture.getSize() == sf::Vector2u(1001, 304));
CHECK(!texture.isSmooth());
CHECK(!texture.isSrgb());
CHECK(!texture.isRepeated());
CHECK(texture.getNativeHandle() != 0);
}
SECTION("Image")
{
SECTION("Subarea of image")
{
const sf::Image image(sf::Vector2u(10, 15));
SECTION("Non-truncated area")
{
const sf::Texture texture(image, false, {{0, 0}, {5, 10}});
CHECK(texture.getSize() == sf::Vector2u(5, 10));
CHECK(texture.getNativeHandle() != 0);
}
SECTION("Truncated area (negative position)")
{
const sf::Texture texture(image, false, {{-5, -5}, {4, 8}});
CHECK(texture.getSize() == sf::Vector2u(4, 8));
CHECK(texture.getNativeHandle() != 0);
}
SECTION("Truncated area (width/height too big)")
{
const sf::Texture texture(image, false, {{5, 5}, {12, 18}});
CHECK(texture.getSize() == sf::Vector2u(5, 10));
CHECK(texture.getNativeHandle() != 0);
}
}
}
}
SECTION("Move semantics") SECTION("Move semantics")
{ {
SECTION("Construction") SECTION("Construction")
@ -58,11 +145,11 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
} }
} }
SECTION("Move semantics (create)") SECTION("Move semantics")
{ {
SECTION("Construction") SECTION("Construction")
{ {
sf::Texture movedTexture = sf::Texture::create({64, 64}).value(); sf::Texture movedTexture(sf::Vector2u(64, 64));
const sf::Texture texture = std::move(movedTexture); const sf::Texture texture = std::move(movedTexture);
CHECK(texture.getSize() == sf::Vector2u(64, 64)); CHECK(texture.getSize() == sf::Vector2u(64, 64));
CHECK(!texture.isSmooth()); CHECK(!texture.isSmooth());
@ -73,8 +160,8 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
SECTION("Assignment") SECTION("Assignment")
{ {
sf::Texture movedTexture = sf::Texture::create({64, 64}).value(); sf::Texture movedTexture(sf::Vector2u(64, 64));
sf::Texture texture = sf::Texture::create({128, 128}).value(); sf::Texture texture(sf::Vector2u(128, 128));
texture = std::move(movedTexture); texture = std::move(movedTexture);
CHECK(texture.getSize() == sf::Vector2u(64, 64)); CHECK(texture.getSize() == sf::Vector2u(64, 64));
CHECK(!texture.isSmooth()); CHECK(!texture.isSmooth());
@ -84,29 +171,6 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
} }
} }
SECTION("create()")
{
SECTION("At least one zero dimension")
{
CHECK(!sf::Texture::create({}));
CHECK(!sf::Texture::create({0, 1}));
CHECK(!sf::Texture::create({1, 0}));
}
SECTION("Valid size")
{
const auto texture = sf::Texture::create({100, 100}).value();
CHECK(texture.getSize() == sf::Vector2u(100, 100));
CHECK(texture.getNativeHandle() != 0);
}
SECTION("Too large")
{
CHECK(!sf::Texture::create({100'000, 100'000}));
CHECK(!sf::Texture::create({1'000'000, 1'000'000}));
}
}
SECTION("resize()") SECTION("resize()")
{ {
sf::Texture texture; sf::Texture texture;
@ -206,72 +270,11 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
} }
} }
SECTION("createFromFile()")
{
const auto texture = sf::Texture::createFromFile("Graphics/sfml-logo-big.png").value();
CHECK(texture.getSize() == sf::Vector2u(1001, 304));
CHECK(!texture.isSmooth());
CHECK(!texture.isSrgb());
CHECK(!texture.isRepeated());
CHECK(texture.getNativeHandle() != 0);
}
SECTION("createFromMemory()")
{
const auto memory = loadIntoMemory("Graphics/sfml-logo-big.png");
const auto texture = sf::Texture::createFromMemory(memory.data(), memory.size()).value();
CHECK(texture.getSize() == sf::Vector2u(1001, 304));
CHECK(!texture.isSmooth());
CHECK(!texture.isSrgb());
CHECK(!texture.isRepeated());
CHECK(texture.getNativeHandle() != 0);
}
SECTION("createFromStream()")
{
auto stream = sf::FileInputStream::create("Graphics/sfml-logo-big.png").value();
const auto texture = sf::Texture::createFromStream(stream).value();
CHECK(texture.getSize() == sf::Vector2u(1001, 304));
CHECK(!texture.isSmooth());
CHECK(!texture.isSrgb());
CHECK(!texture.isRepeated());
CHECK(texture.getNativeHandle() != 0);
}
SECTION("createFromImage()")
{
SECTION("Subarea of image")
{
const sf::Image image(sf::Vector2u(10, 15));
SECTION("Non-truncated area")
{
const auto texture = sf::Texture::createFromImage(image, false, {{0, 0}, {5, 10}}).value();
CHECK(texture.getSize() == sf::Vector2u(5, 10));
CHECK(texture.getNativeHandle() != 0);
}
SECTION("Truncated area (negative position)")
{
const auto texture = sf::Texture::createFromImage(image, false, {{-5, -5}, {4, 8}}).value();
CHECK(texture.getSize() == sf::Vector2u(4, 8));
CHECK(texture.getNativeHandle() != 0);
}
SECTION("Truncated area (width/height too big)")
{
const auto texture = sf::Texture::createFromImage(image, false, {{5, 5}, {12, 18}}).value();
CHECK(texture.getSize() == sf::Vector2u(5, 10));
CHECK(texture.getNativeHandle() != 0);
}
}
}
SECTION("Copy semantics") SECTION("Copy semantics")
{ {
constexpr std::uint8_t red[] = {0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF}; constexpr std::uint8_t red[] = {0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF};
auto texture = sf::Texture::create({1, 2}).value(); sf::Texture texture(sf::Vector2u(1, 2));
texture.update(red); texture.update(red);
SECTION("Construction") SECTION("Construction")
@ -283,7 +286,7 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
SECTION("Assignment") SECTION("Assignment")
{ {
sf::Texture textureCopy = sf::Texture::create({64, 64}).value(); sf::Texture textureCopy(sf::Vector2u(64, 64));
textureCopy = texture; textureCopy = texture;
REQUIRE(textureCopy.getSize() == sf::Vector2u(1, 2)); REQUIRE(textureCopy.getSize() == sf::Vector2u(1, 2));
CHECK(textureCopy.copyToImage().getPixel(sf::Vector2u(0, 1)) == sf::Color::Red); CHECK(textureCopy.copyToImage().getPixel(sf::Vector2u(0, 1)) == sf::Color::Red);
@ -297,14 +300,14 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
SECTION("Pixels") SECTION("Pixels")
{ {
auto texture = sf::Texture::create(sf::Vector2u(1, 1)).value(); sf::Texture texture(sf::Vector2u(1, 1));
texture.update(yellow); texture.update(yellow);
CHECK(texture.copyToImage().getPixel(sf::Vector2u(0, 0)) == sf::Color::Yellow); CHECK(texture.copyToImage().getPixel(sf::Vector2u(0, 0)) == sf::Color::Yellow);
} }
SECTION("Pixels, size and destination") SECTION("Pixels, size and destination")
{ {
auto texture = sf::Texture::create(sf::Vector2u(2, 1)).value(); sf::Texture texture(sf::Vector2u(2, 1));
texture.update(yellow, sf::Vector2u(1, 1), sf::Vector2u(0, 0)); texture.update(yellow, sf::Vector2u(1, 1), sf::Vector2u(0, 0));
texture.update(cyan, sf::Vector2u(1, 1), sf::Vector2u(1, 0)); texture.update(cyan, sf::Vector2u(1, 1), sf::Vector2u(1, 0));
CHECK(texture.copyToImage().getPixel(sf::Vector2u(0, 0)) == sf::Color::Yellow); CHECK(texture.copyToImage().getPixel(sf::Vector2u(0, 0)) == sf::Color::Yellow);
@ -313,19 +316,19 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
SECTION("Another texture") SECTION("Another texture")
{ {
auto otherTexture = sf::Texture::create(sf::Vector2u(1, 1)).value(); sf::Texture otherTexture(sf::Vector2u(1, 1));
otherTexture.update(cyan); otherTexture.update(cyan);
auto texture = sf::Texture::create(sf::Vector2u(1, 1)).value(); sf::Texture texture(sf::Vector2u(1, 1));
texture.update(otherTexture); texture.update(otherTexture);
CHECK(texture.copyToImage().getPixel(sf::Vector2u(0, 0)) == sf::Color::Cyan); CHECK(texture.copyToImage().getPixel(sf::Vector2u(0, 0)) == sf::Color::Cyan);
} }
SECTION("Another texture and destination") SECTION("Another texture and destination")
{ {
auto texture = sf::Texture::create(sf::Vector2u(2, 1)).value(); sf::Texture texture(sf::Vector2u(2, 1));
auto otherTexture1 = sf::Texture::create(sf::Vector2u(1, 1)).value(); sf::Texture otherTexture1(sf::Vector2u(1, 1));
otherTexture1.update(cyan); otherTexture1.update(cyan);
auto otherTexture2 = sf::Texture::create(sf::Vector2u(1, 1)).value(); sf::Texture otherTexture2(sf::Vector2u(1, 1));
otherTexture2.update(yellow); otherTexture2.update(yellow);
texture.update(otherTexture1, sf::Vector2u(0, 0)); texture.update(otherTexture1, sf::Vector2u(0, 0));
texture.update(otherTexture2, sf::Vector2u(1, 0)); texture.update(otherTexture2, sf::Vector2u(1, 0));
@ -335,7 +338,7 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
SECTION("Image") SECTION("Image")
{ {
auto texture = sf::Texture::create(sf::Vector2u(16, 32)).value(); sf::Texture texture(sf::Vector2u(16, 32));
const sf::Image image(sf::Vector2u(16, 32), sf::Color::Red); const sf::Image image(sf::Vector2u(16, 32), sf::Color::Red);
texture.update(image); texture.update(image);
CHECK(texture.copyToImage().getPixel(sf::Vector2u(7, 15)) == sf::Color::Red); CHECK(texture.copyToImage().getPixel(sf::Vector2u(7, 15)) == sf::Color::Red);
@ -343,7 +346,7 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
SECTION("Image and destination") SECTION("Image and destination")
{ {
auto texture = sf::Texture::create(sf::Vector2u(16, 32)).value(); sf::Texture texture(sf::Vector2u(16, 32));
const sf::Image image1(sf::Vector2u(16, 16), sf::Color::Red); const sf::Image image1(sf::Vector2u(16, 16), sf::Color::Red);
texture.update(image1); texture.update(image1);
const sf::Image image2(sf::Vector2u(16, 16), sf::Color::Green); const sf::Image image2(sf::Vector2u(16, 16), sf::Color::Green);
@ -356,7 +359,7 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
SECTION("Set/get smooth") SECTION("Set/get smooth")
{ {
sf::Texture texture = sf::Texture::create({64, 64}).value(); sf::Texture texture(sf::Vector2u(64, 64));
CHECK(!texture.isSmooth()); CHECK(!texture.isSmooth());
texture.setSmooth(true); texture.setSmooth(true);
CHECK(texture.isSmooth()); CHECK(texture.isSmooth());
@ -366,7 +369,7 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
SECTION("Set/get repeated") SECTION("Set/get repeated")
{ {
sf::Texture texture = sf::Texture::create({64, 64}).value(); sf::Texture texture(sf::Vector2u(64, 64));
CHECK(!texture.isRepeated()); CHECK(!texture.isRepeated());
texture.setRepeated(true); texture.setRepeated(true);
CHECK(texture.isRepeated()); CHECK(texture.isRepeated());
@ -376,7 +379,7 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
SECTION("generateMipmap()") SECTION("generateMipmap()")
{ {
sf::Texture texture = sf::Texture::create({100, 100}).value(); sf::Texture texture(sf::Vector2u(100, 100));
CHECK(texture.generateMipmap()); CHECK(texture.generateMipmap());
} }
@ -385,12 +388,12 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
constexpr std::uint8_t blue[] = {0x00, 0x00, 0xFF, 0xFF}; constexpr std::uint8_t blue[] = {0x00, 0x00, 0xFF, 0xFF};
constexpr std::uint8_t green[] = {0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF}; constexpr std::uint8_t green[] = {0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF};
auto texture1 = sf::Texture::create(sf::Vector2u(1, 1), true).value(); sf::Texture texture1(sf::Vector2u(1, 1), true);
texture1.update(blue); texture1.update(blue);
texture1.setSmooth(false); texture1.setSmooth(false);
texture1.setRepeated(true); texture1.setRepeated(true);
auto texture2 = sf::Texture::create(sf::Vector2u(2, 1), false).value(); sf::Texture texture2(sf::Vector2u(2, 1), false);
texture2.update(green); texture2.update(green);
texture2.setSmooth(true); texture2.setSmooth(true);
texture2.setRepeated(false); texture2.setRepeated(false);

View File

@ -72,6 +72,11 @@ TEST_CASE("[System] sf::FileInputStream")
STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::FileInputStream>); STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::FileInputStream>);
} }
const TemporaryFile temporaryFile("Hello world");
char buffer[32];
SECTION("Construction")
{
SECTION("Default constructor") SECTION("Default constructor")
{ {
sf::FileInputStream fileInputStream; sf::FileInputStream fileInputStream;
@ -81,14 +86,23 @@ TEST_CASE("[System] sf::FileInputStream")
CHECK(fileInputStream.getSize() == std::nullopt); CHECK(fileInputStream.getSize() == std::nullopt);
} }
const TemporaryFile temporaryFile("Hello world"); SECTION("File path constructor")
char buffer[32]; {
sf::FileInputStream fileInputStream(temporaryFile.getPath());
CHECK(fileInputStream.read(buffer, 5) == 5);
CHECK(fileInputStream.tell() == 5);
CHECK(fileInputStream.getSize() == 11);
CHECK(std::string_view(buffer, 5) == "Hello"sv);
CHECK(fileInputStream.seek(6) == 6);
CHECK(fileInputStream.tell() == 6);
}
}
SECTION("Move semantics") SECTION("Move semantics")
{ {
SECTION("Move constructor") SECTION("Move constructor")
{ {
auto movedFileInputStream = sf::FileInputStream::create(temporaryFile.getPath()).value(); sf::FileInputStream movedFileInputStream(temporaryFile.getPath());
sf::FileInputStream fileInputStream = std::move(movedFileInputStream); sf::FileInputStream fileInputStream = std::move(movedFileInputStream);
CHECK(fileInputStream.read(buffer, 6) == 6); CHECK(fileInputStream.read(buffer, 6) == 6);
CHECK(fileInputStream.tell() == 6); CHECK(fileInputStream.tell() == 6);
@ -98,9 +112,9 @@ TEST_CASE("[System] sf::FileInputStream")
SECTION("Move assignment") SECTION("Move assignment")
{ {
auto movedFileInputStream = sf::FileInputStream::create(temporaryFile.getPath()).value(); sf::FileInputStream movedFileInputStream(temporaryFile.getPath());
const TemporaryFile temporaryFile2("Hello world the sequel"); const TemporaryFile temporaryFile2("Hello world the sequel");
auto fileInputStream = sf::FileInputStream::create(temporaryFile2.getPath()).value(); sf::FileInputStream fileInputStream(temporaryFile2.getPath());
fileInputStream = std::move(movedFileInputStream); fileInputStream = std::move(movedFileInputStream);
CHECK(fileInputStream.read(buffer, 6) == 6); CHECK(fileInputStream.read(buffer, 6) == 6);
CHECK(fileInputStream.tell() == 6); CHECK(fileInputStream.tell() == 6);
@ -123,7 +137,7 @@ TEST_CASE("[System] sf::FileInputStream")
SECTION("Temporary file stream create") SECTION("Temporary file stream create")
{ {
auto fileInputStream = sf::FileInputStream::create(temporaryFile.getPath()).value(); sf::FileInputStream fileInputStream(temporaryFile.getPath());
CHECK(fileInputStream.read(buffer, 5) == 5); CHECK(fileInputStream.read(buffer, 5) == 5);
CHECK(fileInputStream.tell() == 5); CHECK(fileInputStream.tell() == 5);
CHECK(fileInputStream.getSize() == 11); CHECK(fileInputStream.getSize() == 11);

View File

@ -18,8 +18,17 @@ TEST_CASE("[System] sf::MemoryInputStream")
using namespace std::literals::string_view_literals; using namespace std::literals::string_view_literals;
SECTION("open()") SECTION("Construction")
{ {
SECTION("Null data")
{
sf::MemoryInputStream memoryInputStream(nullptr, 0);
CHECK(memoryInputStream.read(nullptr, 0) == std::nullopt);
CHECK(memoryInputStream.seek(0) == std::nullopt);
CHECK(memoryInputStream.tell() == std::nullopt);
CHECK(memoryInputStream.getSize() == std::nullopt);
}
static constexpr auto input = "hello world"sv; static constexpr auto input = "hello world"sv;
SECTION("Zero length") SECTION("Zero length")

View File

@ -17,6 +17,37 @@ TEST_CASE("[Window] sf::Cursor", runDisplayTests())
STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::Cursor>); STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::Cursor>);
} }
SECTION("Constructor")
{
SECTION("Pixels")
{
static constexpr std::array<std::uint8_t, 4> pixels{};
CHECK_THROWS_AS(sf::Cursor(nullptr, {}, {}), std::runtime_error);
CHECK_THROWS_AS(sf::Cursor(pixels.data(), {0, 1}, {}), std::runtime_error);
CHECK_THROWS_AS(sf::Cursor(pixels.data(), {1, 0}, {}), std::runtime_error);
CHECK_NOTHROW(sf::Cursor(pixels.data(), {1, 1}, {}));
}
SECTION("System")
{
CHECK_NOTHROW(sf::Cursor(sf::Cursor::Type::Hand));
CHECK_NOTHROW(sf::Cursor(sf::Cursor::Type::SizeHorizontal));
CHECK_NOTHROW(sf::Cursor(sf::Cursor::Type::SizeVertical));
CHECK_NOTHROW(sf::Cursor(sf::Cursor::Type::SizeLeft));
CHECK_NOTHROW(sf::Cursor(sf::Cursor::Type::SizeRight));
CHECK_NOTHROW(sf::Cursor(sf::Cursor::Type::SizeTop));
CHECK_NOTHROW(sf::Cursor(sf::Cursor::Type::SizeBottom));
CHECK_NOTHROW(sf::Cursor(sf::Cursor::Type::SizeTopLeft));
CHECK_NOTHROW(sf::Cursor(sf::Cursor::Type::SizeTopRight));
CHECK_NOTHROW(sf::Cursor(sf::Cursor::Type::SizeBottomLeft));
CHECK_NOTHROW(sf::Cursor(sf::Cursor::Type::SizeBottomRight));
CHECK_NOTHROW(sf::Cursor(sf::Cursor::Type::Cross));
CHECK_NOTHROW(sf::Cursor(sf::Cursor::Type::Help));
CHECK_NOTHROW(sf::Cursor(sf::Cursor::Type::NotAllowed));
}
}
SECTION("loadFromPixels()") SECTION("loadFromPixels()")
{ {
static constexpr std::array<std::uint8_t, 4> pixels{}; static constexpr std::array<std::uint8_t, 4> pixels{};

View File

@ -26,15 +26,15 @@ int main()
sf::RenderWindow window(sf::VideoMode({800, 600}), "SFML window"); sf::RenderWindow window(sf::VideoMode({800, 600}), "SFML window");
// Set the Icon // Set the Icon
const auto icon = sf::Image::loadFromFile(resourcePath() / "icon.png").value(); const sf::Image icon(resourcePath() / "icon.png");
window.setIcon(icon); window.setIcon(icon);
// Load a sprite to display // Load a sprite to display
const auto texture = sf::Texture::loadFromFile(resourcePath() / "background.jpg").value(); const sf::Texture texture(resourcePath() / "background.jpg");
sf::Sprite sprite(texture); sf::Sprite sprite(texture);
// Create a graphical text to display // Create a graphical text to display
const auto font = sf::Font::openFromFile(resourcePath() / "tuffy.ttf").value(); const sf::Font font(resourcePath() / "tuffy.ttf");
sf::Text text(font, "Hello SFML", 50); sf::Text text(font, "Hello SFML", 50);
text.setFillColor(sf::Color::Black); text.setFillColor(sf::Color::Black);

View File

@ -24,15 +24,15 @@ int main()
sf::RenderWindow window(sf::VideoMode({800, 600}), "SFML window"); sf::RenderWindow window(sf::VideoMode({800, 600}), "SFML window");
// Set the Icon // Set the Icon
const auto icon = sf::Image::loadFromFile("icon.png").value(); const sf::Image icon("icon.png");
window.setIcon(icon); window.setIcon(icon);
// Load a sprite to display // Load a sprite to display
const auto texture = sf::Texture::loadFromFile("background.jpg").value(); const sf::Texture texture("background.jpg");
sf::Sprite sprite(texture); sf::Sprite sprite(texture);
// Create a graphical text to display // Create a graphical text to display
const auto font = sf::Font::openFromFile("tuffy.ttf").value(); const sf::Font font("tuffy.ttf");
sf::Text text(font, "Hello SFML", 50); sf::Text text(font, "Hello SFML", 50);
text.setFillColor(sf::Color::Black); text.setFillColor(sf::Color::Black);