(Re-)Introduce default constructors and load/open member functions for resource objects that can be reused.

This commit is contained in:
binary1248 2024-07-06 03:38:32 +02:00 committed by Chris Thrasher
parent 6b4da70e15
commit 698f265277
61 changed files with 3167 additions and 934 deletions

View File

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

View File

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

View File

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

View File

@ -91,7 +91,7 @@ int main()
sf::RenderWindow window(sf::VideoMode({windowWidth, windowHeight}), "SFML Island", sf::Style::Titlebar | sf::Style::Close);
window.setVerticalSyncEnabled(true);
const auto font = sf::Font::openFromFile("resources/tuffy.ttf").value();
const auto font = sf::Font::createFromFile("resources/tuffy.ttf").value();
// Create all of our graphics resources
sf::Text hudText(font);
@ -120,7 +120,7 @@ int main()
{
statusText.setString("Shaders and/or Vertex Buffers Unsupported");
}
else if (!(terrainShader = sf::Shader::loadFromFile("resources/terrain.vert", "resources/terrain.frag")))
else if (!(terrainShader = sf::Shader::createFromFile("resources/terrain.vert", "resources/terrain.frag")))
{
statusText.setString("Failed to load shader program");
}

View File

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

View File

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

View File

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

View File

@ -278,11 +278,11 @@ private:
////////////////////////////////////////////////////////////
std::optional<Pixelate> tryLoadPixelate()
{
auto texture = sf::Texture::loadFromFile("resources/background.jpg");
auto texture = sf::Texture::createFromFile("resources/background.jpg");
if (!texture.has_value())
return std::nullopt;
auto shader = sf::Shader::loadFromFile("resources/pixelate.frag", sf::Shader::Type::Fragment);
auto shader = sf::Shader::createFromFile("resources/pixelate.frag", sf::Shader::Type::Fragment);
if (!shader.has_value())
return std::nullopt;
@ -291,7 +291,7 @@ std::optional<Pixelate> tryLoadPixelate()
std::optional<WaveBlur> tryLoadWaveBlur(const sf::Font& font)
{
auto shader = sf::Shader::loadFromFile("resources/wave.vert", "resources/blur.frag");
auto shader = sf::Shader::createFromFile("resources/wave.vert", "resources/blur.frag");
if (!shader.has_value())
return std::nullopt;
@ -300,7 +300,7 @@ std::optional<WaveBlur> tryLoadWaveBlur(const sf::Font& font)
std::optional<StormBlink> tryLoadStormBlink()
{
auto shader = sf::Shader::loadFromFile("resources/storm.vert", "resources/blink.frag");
auto shader = sf::Shader::createFromFile("resources/storm.vert", "resources/blink.frag");
if (!shader.has_value())
return std::nullopt;
@ -317,21 +317,21 @@ std::optional<Edge> tryLoadEdge()
surface->setSmooth(true);
// Load the background texture
auto backgroundTexture = sf::Texture::loadFromFile("resources/sfml.png");
auto backgroundTexture = sf::Texture::createFromFile("resources/sfml.png");
if (!backgroundTexture.has_value())
return std::nullopt;
backgroundTexture->setSmooth(true);
// Load the entity texture
auto entityTexture = sf::Texture::loadFromFile("resources/devices.png");
auto entityTexture = sf::Texture::createFromFile("resources/devices.png");
if (!entityTexture.has_value())
return std::nullopt;
entityTexture->setSmooth(true);
// Load the shader
auto shader = sf::Shader::loadFromFile("resources/edge.frag", sf::Shader::Type::Fragment);
auto shader = sf::Shader::createFromFile("resources/edge.frag", sf::Shader::Type::Fragment);
if (!shader.has_value())
return std::nullopt;
@ -350,14 +350,14 @@ std::optional<Geometry> tryLoadGeometry()
return std::nullopt;
// Load the logo texture
auto logoTexture = sf::Texture::loadFromFile("resources/logo.png");
auto logoTexture = sf::Texture::createFromFile("resources/logo.png");
if (!logoTexture.has_value())
return std::nullopt;
logoTexture->setSmooth(true);
// Load the shader
auto shader = sf::Shader::loadFromFile("resources/billboard.vert",
auto shader = sf::Shader::createFromFile("resources/billboard.vert",
"resources/billboard.geom",
"resources/billboard.frag");
if (!shader.has_value())
@ -394,7 +394,7 @@ int main()
window.setVerticalSyncEnabled(true);
// Open the application font
const auto font = sf::Font::openFromFile("resources/tuffy.ttf").value();
const auto font = sf::Font::createFromFile("resources/tuffy.ttf").value();
// Create the effects
std::optional pixelateEffect = tryLoadPixelate();
@ -418,7 +418,7 @@ int main()
std::size_t current = 0;
// Create the messages background
const auto textBackgroundTexture = sf::Texture::loadFromFile("resources/text-background.png").value();
const auto textBackgroundTexture = sf::Texture::createFromFile("resources/text-background.png").value();
sf::Sprite textBackground(textBackgroundTexture);
textBackground.setPosition({0.f, 520.f});
textBackground.setColor(sf::Color(255, 255, 255, 200));

View File

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

View File

@ -115,7 +115,7 @@ public:
m_listener.setFillColor(sf::Color::Red);
// Load the music file
if (!(m_music = sf::Music::openFromFile(resourcesDir() / "doodle_pop.ogg")))
if (!(m_music = sf::Music::createFromFile(resourcesDir() / "doodle_pop.ogg")))
{
std::cerr << "Failed to load " << (resourcesDir() / "doodle_pop.ogg").string() << std::endl;
std::abort();
@ -177,7 +177,7 @@ public:
m_volumeText(getFont(), "Volume: " + std::to_string(m_volume))
{
// Load the music file
if (!(m_music = sf::Music::openFromFile(resourcesDir() / "doodle_pop.ogg")))
if (!(m_music = sf::Music::createFromFile(resourcesDir() / "doodle_pop.ogg")))
{
std::cerr << "Failed to load " << (resourcesDir() / "doodle_pop.ogg").string() << std::endl;
std::abort();
@ -283,7 +283,7 @@ public:
makeCone(m_soundConeInner, innerConeAngle);
// Load the music file
if (!(m_music = sf::Music::openFromFile(resourcesDir() / "doodle_pop.ogg")))
if (!(m_music = sf::Music::createFromFile(resourcesDir() / "doodle_pop.ogg")))
{
std::cerr << "Failed to load " << (resourcesDir() / "doodle_pop.ogg").string() << std::endl;
std::abort();
@ -677,7 +677,7 @@ protected:
m_instructions.setPosition({windowWidth / 2.f - 250.f, windowHeight * 3.f / 4.f});
// Load the music file
if (!(m_music = sf::Music::openFromFile(resourcesDir() / "doodle_pop.ogg")))
if (!(m_music = sf::Music::createFromFile(resourcesDir() / "doodle_pop.ogg")))
{
std::cerr << "Failed to load " << (resourcesDir() / "doodle_pop.ogg").string() << std::endl;
std::abort();
@ -1077,7 +1077,7 @@ int main()
window.setVerticalSyncEnabled(true);
// Open the application font and pass it to the Effect class
const auto font = sf::Font::openFromFile(resourcesDir() / "tuffy.ttf").value();
const auto font = sf::Font::createFromFile(resourcesDir() / "tuffy.ttf").value();
Effect::setFont(font);
// Create the effects
@ -1106,7 +1106,7 @@ int main()
effects[current]->start();
// Create the messages background
const auto textBackgroundTexture = sf::Texture::loadFromFile(resourcesDir() / "text-background.png").value();
const auto textBackgroundTexture = sf::Texture::createFromFile(resourcesDir() / "text-background.png").value();
sf::Sprite textBackground(textBackgroundTexture);
textBackground.setPosition({0.f, 520.f});
textBackground.setColor(sf::Color(255, 255, 255, 200));

View File

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

View File

@ -923,7 +923,7 @@ public:
// Use the vertex shader SPIR-V code to create a vertex shader module
{
auto file = sf::FileInputStream::open("resources/shader.vert.spv");
auto file = sf::FileInputStream::create("resources/shader.vert.spv");
if (!file)
{
vulkanAvailable = false;
@ -951,7 +951,7 @@ public:
// Use the fragment shader SPIR-V code to create a fragment shader module
{
auto file = sf::FileInputStream::open("resources/shader.frag.spv");
auto file = sf::FileInputStream::create("resources/shader.frag.spv");
if (!file)
{
vulkanAvailable = false;
@ -1801,7 +1801,7 @@ public:
void setupTextureImage()
{
// Load the image data
const auto maybeImageData = sf::Image::loadFromFile("resources/logo.png");
const auto maybeImageData = sf::Image::createFromFile("resources/logo.png");
if (!maybeImageData)
{

View File

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

View File

@ -52,6 +52,15 @@ class InputStream;
class SFML_AUDIO_API InputSoundFile
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Construct an input sound file that is not associated
/// with a file to read.
///
////////////////////////////////////////////////////////////
InputSoundFile() = default;
////////////////////////////////////////////////////////////
/// \brief Open a sound file from the disk for reading
///
@ -65,13 +74,58 @@ public:
///
/// \param filename Path of the sound file to load
///
/// \return True if the file was successfully opened
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool openFromFile(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Open 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 True if the file was successfully opened
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool openFromMemory(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Open 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 True if the file was successfully opened
///
////////////////////////////////////////////////////////////
[[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> openFromFile(const std::filesystem::path& filename);
[[nodiscard]] static std::optional<InputSoundFile> createFromFile(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Open a sound file in memory for reading
/// \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.
@ -82,10 +136,10 @@ public:
/// \return Input sound file if the file was successfully opened, otherwise `std::nullopt`
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<InputSoundFile> openFromMemory(const void* data, std::size_t sizeInBytes);
[[nodiscard]] static std::optional<InputSoundFile> createFromMemory(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Open a sound file from a custom stream for reading
/// \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.
@ -95,7 +149,7 @@ public:
/// \return Input sound file if the file was successfully opened, otherwise `std::nullopt`
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<InputSoundFile> openFromStream(InputStream& stream);
[[nodiscard]] static std::optional<InputSoundFile> createFromStream(InputStream& stream);
////////////////////////////////////////////////////////////
/// \brief Get the total number of audio samples in the file
@ -212,14 +266,6 @@ public:
void close();
private:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Useful for implementing close()
///
////////////////////////////////////////////////////////////
InputSoundFile() = default;
////////////////////////////////////////////////////////////
/// \brief Deleter for input streams that only conditionally deletes
///
@ -237,16 +283,6 @@ private:
bool owned{true};
};
////////////////////////////////////////////////////////////
/// \brief Constructor from reader, stream, and attributes
///
////////////////////////////////////////////////////////////
InputSoundFile(std::unique_ptr<SoundFileReader>&& reader,
std::unique_ptr<InputStream, StreamDeleter>&& stream,
std::uint64_t sampleCount,
unsigned int sampleRate,
std::vector<SoundChannel>&& channelMap);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
@ -275,7 +311,7 @@ private:
/// Usage example:
/// \code
/// // Open a sound file
/// auto file = sf::InputSoundFile::openFromFile("music.ogg").value();
/// auto file = sf::InputSoundFile::createFromFile("music.ogg").value();
///
/// // Print the sound attributes
/// std::cout << "duration: " << file.getDuration().asSeconds() << '\n'

View File

@ -66,6 +66,14 @@ public:
// Define the relevant Span types
using TimeSpan = Span<Time>;
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Construct an empty music that does not contain any data.
///
////////////////////////////////////////////////////////////
Music();
////////////////////////////////////////////////////////////
/// \brief Destructor
///
@ -98,12 +106,12 @@ public:
///
/// \param filename Path of the music file to open
///
/// \return Music if loading succeeded, `std::nullopt` if it failed
/// \return True if loading succeeded, false if it failed
///
/// \see openFromMemory, openFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Music> openFromFile(const std::filesystem::path& filename);
[[nodiscard]] bool openFromFile(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Open a music from an audio file in memory
@ -121,12 +129,12 @@ public:
/// \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
/// \return True if loading succeeded, false if it failed
///
/// \see openFromFile, openFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Music> openFromMemory(const void* data, std::size_t sizeInBytes);
[[nodiscard]] bool openFromMemory(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Open a music from an audio file in a custom stream
@ -142,12 +150,77 @@ public:
///
/// \param stream Source stream to read from
///
/// \return Music if loading succeeded, `std::nullopt` if it failed
/// \return True if loading succeeded, false if it failed
///
/// \see openFromFile, openFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Music> 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
@ -231,19 +304,6 @@ protected:
std::optional<std::uint64_t> onLoop() override;
private:
////////////////////////////////////////////////////////////
/// \brief Try opening the music file from an optional input sound file
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Music> tryOpenFromInputSoundFile(std::optional<InputSoundFile>&& optFile,
const char* errorContext);
////////////////////////////////////////////////////////////
/// \brief Initialize the internal state after loading a new music
///
////////////////////////////////////////////////////////////
explicit Music(InputSoundFile&& file);
////////////////////////////////////////////////////////////
/// \brief Helper to convert an sf::Time to a sample position
///
@ -300,7 +360,7 @@ private:
/// Usage example:
/// \code
/// // Open a music from an audio file
/// auto music = sf::Music::openFromFile("music.ogg").value();
/// auto music = sf::Music::createFromFile("music.ogg").value();
///
/// // Change some parameters
/// music.setPosition({0, 1, 10}); // change its 3D position

View File

@ -49,6 +49,15 @@ namespace sf
class SFML_AUDIO_API OutputSoundFile
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Construct an output sound file that is not associated
/// with a file to write.
///
////////////////////////////////////////////////////////////
OutputSoundFile() = default;
////////////////////////////////////////////////////////////
/// \brief Open the sound file from the disk for writing
///
@ -59,10 +68,28 @@ public:
/// \param channelCount Number of channels in the sound
/// \param channelMap Map of position in sample frame to sound channel
///
/// \return True if the file was successfully opened
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool openFromFile(const std::filesystem::path& filename,
unsigned int sampleRate,
unsigned int channelCount,
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> openFromFile(
[[nodiscard]] static std::optional<OutputSoundFile> createFromFile(
const std::filesystem::path& filename,
unsigned int sampleRate,
unsigned int channelCount,
@ -84,12 +111,6 @@ public:
void close();
private:
////////////////////////////////////////////////////////////
/// \brief Constructor from writer
///
////////////////////////////////////////////////////////////
explicit OutputSoundFile(std::unique_ptr<SoundFileWriter>&& writer);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
@ -111,7 +132,7 @@ private:
/// Usage example:
/// \code
/// // Create a sound file, ogg/vorbis format, 44100 Hz, stereo
/// auto file = sf::OutputSoundFile::openFromFile("music.ogg", 44100, 2).value();
/// auto file = sf::OutputSoundFile::createFromFile("music.ogg", 44100, 2).value();
///
/// while (...)
/// {

View File

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

View File

@ -55,6 +55,15 @@ class InputStream;
class SFML_AUDIO_API SoundBuffer
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Construct an empty sound buffer that does not contain
/// any samples.
///
////////////////////////////////////////////////////////////
SoundBuffer() = default;
////////////////////////////////////////////////////////////
/// \brief Copy constructor
///
@ -77,12 +86,12 @@ public:
///
/// \param filename Path of the sound file to load
///
/// \return Sound buffer if loading succeeded, `std::nullopt` if it failed
/// \return True if loading succeeded, false if it failed
///
/// \see loadFromMemory, loadFromStream, loadFromSamples, saveToFile
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<SoundBuffer> loadFromFile(const std::filesystem::path& filename);
[[nodiscard]] bool loadFromFile(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Load the sound buffer from a file in memory
@ -93,12 +102,12 @@ public:
/// \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
/// \return True if loading succeeded, false if it failed
///
/// \see loadFromFile, loadFromStream, loadFromSamples
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<SoundBuffer> loadFromMemory(const void* data, std::size_t sizeInBytes);
[[nodiscard]] bool loadFromMemory(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Load the sound buffer from a custom stream
@ -108,12 +117,12 @@ public:
///
/// \param stream Source stream to read from
///
/// \return Sound buffer if loading succeeded, `std::nullopt` if it failed
/// \return True if loading succeeded, false if it failed
///
/// \see loadFromFile, loadFromMemory, loadFromSamples
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<SoundBuffer> loadFromStream(InputStream& stream);
[[nodiscard]] bool loadFromStream(InputStream& stream);
////////////////////////////////////////////////////////////
/// \brief Load the sound buffer from an array of audio samples
@ -126,12 +135,80 @@ public:
/// \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
/// \return True if loading succeeded, false if it failed
///
/// \see loadFromFile, loadFromMemory, saveToFile
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<SoundBuffer> loadFromSamples(
[[nodiscard]] bool loadFromSamples(const std::int16_t* samples,
std::uint64_t sampleCount,
unsigned int channelCount,
unsigned int sampleRate,
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,
@ -148,7 +225,7 @@ public:
///
/// \return True if saving succeeded, false if it failed
///
/// \see loadFromFile, loadFromMemory, loadFromSamples
/// \see createFromFile, createFromMemory, createFromSamples
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool saveToFile(const std::filesystem::path& filename) const;
@ -243,12 +320,6 @@ public:
private:
friend class Sound;
////////////////////////////////////////////////////////////
/// \brief Construct from vector of samples
///
////////////////////////////////////////////////////////////
explicit SoundBuffer(std::vector<std::int16_t>&& samples);
////////////////////////////////////////////////////////////
/// \brief Initialize the internal state after loading a new sound
///
@ -257,7 +328,7 @@ private:
/// \return True on successful initialization, false on failure
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<SoundBuffer> initialize(InputSoundFile& file);
[[nodiscard]] bool initialize(InputSoundFile& file);
////////////////////////////////////////////////////////////
/// \brief Update the internal buffer with the cached audio samples
@ -318,7 +389,7 @@ private:
/// are like texture pixels, and a sf::SoundBuffer is similar to
/// a sf::Texture.
///
/// A sound buffer can be loaded from a file (see loadFromFile()
/// A sound buffer can be loaded from a file (see createFromFile()
/// for the complete list of supported formats), from memory, from
/// a custom stream (see sf::InputStream) or directly from an array
/// of samples. It can also be saved back to a file.
@ -344,7 +415,7 @@ private:
/// Usage example:
/// \code
/// // Load a new sound buffer from a file
/// const auto buffer = sf::SoundBuffer::loadFromFile("sound.wav").value();
/// const auto buffer = sf::SoundBuffer::createFromFile("sound.wav").value();
///
/// // Create a sound source bound to the buffer
/// sf::Sound sound1(buffer);

View File

@ -98,7 +98,7 @@ private:
// Member data
////////////////////////////////////////////////////////////
std::vector<std::int16_t> m_samples; //!< Temporary sample buffer to hold the recorded data
std::optional<SoundBuffer> m_buffer; //!< Sound buffer that will contain the recorded data
SoundBuffer m_buffer; //!< Sound buffer that will contain the recorded data
};
} // namespace sf

View File

@ -73,6 +73,14 @@ public:
std::string family; //!< The font family
};
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Construct an empty font that does not contain any glyphs.
///
////////////////////////////////////////////////////////////
Font() = default;
////////////////////////////////////////////////////////////
/// \brief Open the font from a file
///
@ -84,19 +92,84 @@ public:
///
/// \warning SFML cannot preload all the font data in this
/// function, so the file has to remain accessible until
/// the sf::Font object loads a new font or is destroyed.
///
/// \param filename Path of the font file to load
///
/// \return True if loading succeeded, false if it failed
///
/// \see openFromMemory, openFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool openFromFile(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Open 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 loads 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
///
/// \return True if loading succeeded, false if it failed
///
/// \see openFromFile, openFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool openFromMemory(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Open 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 loads a new font or is destroyed.
///
/// \param stream Source stream to read from
///
/// \return True if loading succeeded, false if it failed
///
/// \see openFromFile, openFromMemory
///
////////////////////////////////////////////////////////////
[[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 openFromMemory, openFromStream
/// \see createFromMemory, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Font> openFromFile(const std::filesystem::path& filename);
[[nodiscard]] static std::optional<Font> createFromFile(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Open the font from a file in memory
/// \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.
@ -110,13 +183,13 @@ public:
///
/// \return Font if opening succeeded, `std::nullopt` if it failed
///
/// \see openFromFile, openFromStream
/// \see createFromFile, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Font> openFromMemory(const void* data, std::size_t sizeInBytes);
[[nodiscard]] static std::optional<Font> createFromMemory(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Open the font from a custom stream
/// \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.
@ -129,10 +202,10 @@ public:
///
/// \return Font if opening succeeded, `std::nullopt` if it failed
///
/// \see openFromFile, openFromMemory
/// \see createFromFile, createFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Font> openFromStream(InputStream& stream);
[[nodiscard]] static std::optional<Font> createFromStream(InputStream& stream);
////////////////////////////////////////////////////////////
/// \brief Get the font information
@ -315,8 +388,7 @@ private:
////////////////////////////////////////////////////////////
struct Page
{
[[nodiscard]] static std::optional<Page> create(bool smooth);
explicit Page(Texture&& texture);
explicit Page(bool smooth);
GlyphTable glyphs; //!< Table mapping code points to their corresponding glyph
Texture texture; //!< Texture containing the pixels of the glyphs
@ -324,6 +396,12 @@ private:
std::vector<Row> rows; //!< List containing the position of all the existing rows
};
////////////////////////////////////////////////////////////
/// \brief Free all the internal resources
///
////////////////////////////////////////////////////////////
void cleanup();
////////////////////////////////////////////////////////////
/// \brief Find or create the glyphs page corresponding to the given character size
///
@ -374,12 +452,6 @@ private:
struct FontHandles;
using PageTable = std::unordered_map<unsigned int, Page>; //!< Table mapping a character size to its page (texture)
////////////////////////////////////////////////////////////
/// \brief Create a font from font handles and a family name
///
////////////////////////////////////////////////////////////
Font(std::shared_ptr<FontHandles>&& fontHandles, std::string&& familyName);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
@ -402,7 +474,7 @@ private:
///
/// Fonts can be opened from a file, from memory or from a custom
/// stream, and supports the most common types of fonts. See
/// the openFromFile function for the complete list of supported formats.
/// the createFromFile function for the complete list of supported formats.
///
/// Once it is opened, a sf::Font instance provides three
/// types of information about the font:
@ -433,7 +505,7 @@ private:
/// Usage example:
/// \code
/// // Open a new font
/// const auto font = sf::Font::openFromFile("arial.ttf").value();
/// const auto font = sf::Font::createFromFile("arial.ttf").value();
///
/// // Create a text which uses our font
/// sf::Text text1(font);

View File

@ -54,6 +54,14 @@ class InputStream;
class SFML_GRAPHICS_API Image
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Constructs an image with width 0 and height 0.
///
////////////////////////////////////////////////////////////
Image() = default;
////////////////////////////////////////////////////////////
/// \brief Construct the image and fill it with a unique color
///
@ -77,6 +85,81 @@ public:
////////////////////////////////////////////////////////////
Image(Vector2u size, const std::uint8_t* pixels);
////////////////////////////////////////////////////////////
/// \brief Resize the image and fill it with a unique color
///
/// \param size Width and height of the image
/// \param color Fill color
///
////////////////////////////////////////////////////////////
void resize(Vector2u size, Color color = Color::Black);
////////////////////////////////////////////////////////////
/// \brief Resize the image from an array of pixels
///
/// The \a pixel array is assumed to contain 32-bits RGBA pixels,
/// and have the given \a width and \a height. If not, this is
/// an undefined behavior.
/// If \a pixels is null, an empty image is created.
///
/// \param size Width and height of the image
/// \param pixels Array of pixels to copy to the image
///
////////////////////////////////////////////////////////////
void resize(Vector2u size, const std::uint8_t* pixels);
////////////////////////////////////////////////////////////
/// \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 True if loading was successful
///
/// \see loadFromMemory, loadFromStream, saveToFile
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromFile(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 True if loading was successful
///
/// \see loadFromFile, loadFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromMemory(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 True if loading was successful
///
/// \see loadFromFile, loadFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromStream(InputStream& stream);
////////////////////////////////////////////////////////////
/// \brief Load the image from a file on disk
///
@ -89,10 +172,10 @@ public:
///
/// \return Image if loading was successful, `std::nullopt` otherwise
///
/// \see loadFromMemory, loadFromStream, saveToFile
/// \see createFromMemory, createFromStream, saveToFile
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Image> loadFromFile(const std::filesystem::path& filename);
[[nodiscard]] static std::optional<Image> createFromFile(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Load the image from a file in memory
@ -107,10 +190,10 @@ public:
///
/// \return Image if loading was successful, `std::nullopt` otherwise
///
/// \see loadFromFile, loadFromStream
/// \see createFromFile, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Image> loadFromMemory(const void* data, std::size_t size);
[[nodiscard]] static std::optional<Image> createFromMemory(const void* data, std::size_t size);
////////////////////////////////////////////////////////////
/// \brief Load the image from a custom stream
@ -124,10 +207,10 @@ public:
///
/// \return Image if loading was successful, `std::nullopt` otherwise
///
/// \see loadFromFile, loadFromMemory
/// \see createFromFile, createFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Image> loadFromStream(InputStream& stream);
[[nodiscard]] static std::optional<Image> createFromStream(InputStream& stream);
////////////////////////////////////////////////////////////
/// \brief Save the image to a file on disk
@ -141,7 +224,7 @@ public:
///
/// \return True if saving was successful
///
/// \see create, loadFromFile, loadFromMemory
/// \see create, createFromFile, createFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool saveToFile(const std::filesystem::path& filename) const;
@ -159,7 +242,7 @@ public:
/// \return Buffer with encoded data if saving was successful,
/// otherwise std::nullopt
///
/// \see create, loadFromFile, loadFromMemory, saveToFile
/// \see create, createFromFile, createFromMemory, saveToFile
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::optional<std::vector<std::uint8_t>> saveToMemory(std::string_view format) const;
@ -278,12 +361,6 @@ public:
void flipVertically();
private:
////////////////////////////////////////////////////////////
/// \brief Directly initialize data members
///
////////////////////////////////////////////////////////////
Image(Vector2u size, std::vector<std::uint8_t>&& pixels);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
@ -309,7 +386,7 @@ private:
/// channels -- just like a sf::Color.
/// All the functions that return an array of pixels follow
/// this rule, and all parameters that you pass to sf::Image
/// functions (such as loadFromMemory) must use this
/// functions (such as createFromMemory) must use this
/// representation as well.
///
/// A sf::Image can be copied, but it is a heavy resource and
@ -319,7 +396,7 @@ private:
/// Usage example:
/// \code
/// // Load an image file from a file
/// const auto background = sf::Image::loadFromFile("background.jpg").value();
/// const auto background = sf::Image::createFromFile("background.jpg").value();
///
/// // Create a 20x20 image filled with black color
/// sf::Image image({20, 20}, sf::Color::Black);

View File

@ -54,6 +54,16 @@ class RenderTextureImpl;
class SFML_GRAPHICS_API RenderTexture : public RenderTarget
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Constructs a render-texture with width 0 and height 0.
///
/// \see resize
///
////////////////////////////////////////////////////////////
RenderTexture();
////////////////////////////////////////////////////////////
/// \brief Destructor
///
@ -84,6 +94,25 @@ public:
////////////////////////////////////////////////////////////
RenderTexture& operator=(RenderTexture&&) noexcept;
////////////////////////////////////////////////////////////
/// \brief Resize 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 resizing, 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 True if resizing has been successful
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool resize(Vector2u size, const ContextSettings& settings = ContextSettings());
////////////////////////////////////////////////////////////
/// \brief Create the render-texture
///
@ -240,12 +269,6 @@ public:
[[nodiscard]] const Texture& getTexture() const;
private:
////////////////////////////////////////////////////////////
/// \brief Construct from texture
///
////////////////////////////////////////////////////////////
explicit RenderTexture(Texture&& texture);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////

View File

@ -266,9 +266,9 @@ private:
/// sf::RenderWindow window(sf::VideoMode({800, 600}), "SFML OpenGL");
///
/// // Create a sprite and a text to display
/// const auto texture = sf::Texture::loadFromFile("circle.png").value();
/// const auto texture = sf::Texture::createFromFile("circle.png").value();
/// sf::Sprite sprite(texture);
/// const auto font = sf::Font::openFromFile("arial.ttf").value();
/// const auto font = sf::Font::createFromFile("arial.ttf").value();
/// sf::Text text(font);
/// ...
///

View File

@ -85,6 +85,17 @@ public:
// NOLINTNEXTLINE(readability-identifier-naming)
static inline CurrentTextureType CurrentTexture;
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// This constructor creates an empty shader.
///
/// Binding an empty shader has the same effect as not
/// binding any shader.
///
////////////////////////////////////////////////////////////
Shader() = default;
////////////////////////////////////////////////////////////
/// \brief Destructor
///
@ -115,6 +126,202 @@ public:
////////////////////////////////////////////////////////////
Shader& operator=(Shader&& right) noexcept;
////////////////////////////////////////////////////////////
/// \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 True if loading succeeded, false if it failed
///
/// \see loadFromMemory, loadFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromFile(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 True if loading succeeded, false if it failed
///
/// \see loadFromMemory, loadFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromFile(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 True if loading succeeded, false if it failed
///
/// \see loadFromMemory, loadFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromFile(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 True if loading succeeded, false if it failed
///
/// \see loadFromFile, loadFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromMemory(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 True if loading succeeded, false if it failed
///
/// \see loadFromFile, loadFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromMemory(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 True if loading succeeded, false if it failed
///
/// \see loadFromFile, loadFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromMemory(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 True if loading succeeded, false if it failed
///
/// \see loadFromFile, loadFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromStream(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 True if loading succeeded, false if it failed
///
/// \see loadFromFile, loadFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromStream(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 True if loading succeeded, false if it failed
///
/// \see loadFromFile, loadFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromStream(InputStream& vertexShaderStream,
InputStream& geometryShaderStream,
InputStream& fragmentShaderStream);
////////////////////////////////////////////////////////////
/// \brief Load the vertex, geometry or fragment shader from a file
@ -132,10 +339,10 @@ public:
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see loadFromMemory, loadFromStream
/// \see createFromMemory, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> loadFromFile(const std::filesystem::path& filename, Type type);
[[nodiscard]] static std::optional<Shader> createFromFile(const std::filesystem::path& filename, Type type);
////////////////////////////////////////////////////////////
/// \brief Load both the vertex and fragment shaders from files
@ -153,10 +360,10 @@ public:
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see loadFromMemory, loadFromStream
/// \see createFromMemory, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> loadFromFile(const std::filesystem::path& vertexShaderFilename,
[[nodiscard]] static std::optional<Shader> createFromFile(const std::filesystem::path& vertexShaderFilename,
const std::filesystem::path& fragmentShaderFilename);
////////////////////////////////////////////////////////////
@ -176,10 +383,10 @@ public:
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see loadFromMemory, loadFromStream
/// \see createFromMemory, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> loadFromFile(const std::filesystem::path& vertexShaderFilename,
[[nodiscard]] static std::optional<Shader> createFromFile(const std::filesystem::path& vertexShaderFilename,
const std::filesystem::path& geometryShaderFilename,
const std::filesystem::path& fragmentShaderFilename);
@ -198,10 +405,10 @@ public:
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see loadFromFile, loadFromStream
/// \see createFromFile, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> loadFromMemory(std::string_view shader, Type type);
[[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
@ -219,10 +426,10 @@ public:
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see loadFromFile, loadFromStream
/// \see createFromFile, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> loadFromMemory(std::string_view vertexShader, std::string_view fragmentShader);
[[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
@ -241,10 +448,10 @@ public:
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see loadFromFile, loadFromStream
/// \see createFromFile, createFromStream
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> loadFromMemory(std::string_view vertexShader,
[[nodiscard]] static std::optional<Shader> createFromMemory(std::string_view vertexShader,
std::string_view geometryShader,
std::string_view fragmentShader);
@ -263,10 +470,10 @@ public:
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see loadFromFile, loadFromMemory
/// \see createFromFile, createFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> loadFromStream(InputStream& stream, Type type);
[[nodiscard]] static std::optional<Shader> createFromStream(InputStream& stream, Type type);
////////////////////////////////////////////////////////////
/// \brief Load both the vertex and fragment shaders from custom streams
@ -284,10 +491,10 @@ public:
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see loadFromFile, loadFromMemory
/// \see createFromFile, createFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> loadFromStream(InputStream& vertexShaderStream,
[[nodiscard]] static std::optional<Shader> createFromStream(InputStream& vertexShaderStream,
InputStream& fragmentShaderStream);
////////////////////////////////////////////////////////////
@ -307,10 +514,10 @@ public:
///
/// \return Shader if loading succeeded, `std::nullopt` if it failed
///
/// \see loadFromFile, loadFromMemory
/// \see createFromFile, createFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> loadFromStream(InputStream& vertexShaderStream,
[[nodiscard]] static std::optional<Shader> createFromStream(InputStream& vertexShaderStream,
InputStream& geometryShaderStream,
InputStream& fragmentShaderStream);
@ -645,12 +852,6 @@ public:
[[nodiscard]] static bool isGeometryAvailable();
private:
////////////////////////////////////////////////////////////
/// \brief Construct from shader program
///
////////////////////////////////////////////////////////////
explicit Shader(unsigned int shaderProgram);
////////////////////////////////////////////////////////////
/// \brief Compile the shader(s) and create the program
///
@ -661,10 +862,10 @@ private:
/// \param geometryShaderCode Source code of the geometry shader
/// \param fragmentShaderCode Source code of the fragment shader
///
/// \return Shader on success, `std::nullopt` if any error happened
/// \return True on success, false if any error happened
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Shader> compile(std::string_view vertexShaderCode,
[[nodiscard]] bool compile(std::string_view vertexShaderCode,
std::string_view geometryShaderCode,
std::string_view fragmentShaderCode);

View File

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

View File

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

View File

@ -56,6 +56,14 @@ class Image;
class SFML_GRAPHICS_API Texture : GlResource
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Creates a texture with width 0 and height 0.
///
////////////////////////////////////////////////////////////
Texture();
////////////////////////////////////////////////////////////
/// \brief Destructor
///
@ -89,13 +97,152 @@ public:
Texture& operator=(Texture&&) noexcept;
////////////////////////////////////////////////////////////
/// \brief Create the texture
/// \brief Resize the texture
///
/// If this function fails, the texture is left unchanged.
///
/// \param size Width and height of the texture
/// \param sRgb True to enable sRGB conversion, false to disable it
///
/// \return True if resizing was successful
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool resize(Vector2u size, bool sRgb = false);
////////////////////////////////////////////////////////////
/// \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
/// 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 True if loading was successful
///
/// \see loadFromMemory, loadFromStream, loadFromImage
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromFile(const std::filesystem::path& filename, bool sRgb = false, const IntRect& area = IntRect());
////////////////////////////////////////////////////////////
/// \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
/// 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 True if loading was successful
///
/// \see loadFromFile, loadFromStream, loadFromImage
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromMemory(const void* data, std::size_t size, bool sRgb = false, const IntRect& area = IntRect());
////////////////////////////////////////////////////////////
/// \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
/// 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 True if loading was successful
///
/// \see loadFromFile, loadFromMemory, loadFromImage
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromStream(InputStream& stream, bool sRgb = false, const IntRect& area = IntRect());
////////////////////////////////////////////////////////////
/// \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 True if loading was successful
///
/// \see loadFromFile, loadFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromImage(const Image& image, bool sRgb = false, const IntRect& area = IntRect());
////////////////////////////////////////////////////////////
/// \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`
///
////////////////////////////////////////////////////////////
@ -121,10 +268,10 @@ public:
///
/// \return Texture if loading was successful, otherwise `std::nullopt`
///
/// \see loadFromMemory, loadFromStream, loadFromImage
/// \see createFromMemory, createFromStream, createFromImage
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Texture> loadFromFile(const std::filesystem::path& filename,
[[nodiscard]] static std::optional<Texture> createFromFile(const std::filesystem::path& filename,
bool sRgb = false,
const IntRect& area = {});
@ -149,10 +296,10 @@ public:
///
/// \return Texture if loading was successful, otherwise `std::nullopt`
///
/// \see loadFromFile, loadFromStream, loadFromImage
/// \see createFromFile, createFromStream, createFromImage
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Texture> loadFromMemory(
[[nodiscard]] static std::optional<Texture> createFromMemory(
const void* data,
std::size_t size,
bool sRgb = false,
@ -178,10 +325,12 @@ public:
///
/// \return Texture if loading was successful, otherwise `std::nullopt`
///
/// \see loadFromFile, loadFromMemory, loadFromImage
/// \see createFromFile, createFromMemory, createFromImage
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Texture> loadFromStream(InputStream& stream, bool sRgb = false, const IntRect& area = {});
[[nodiscard]] static std::optional<Texture> createFromStream(InputStream& stream,
bool sRgb = false,
const IntRect& area = {});
////////////////////////////////////////////////////////////
/// \brief Load the texture from an image
@ -203,10 +352,10 @@ public:
///
/// \return Texture if loading was successful, otherwise `std::nullopt`
///
/// \see loadFromFile, loadFromMemory
/// \see createFromFile, createFromMemory
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Texture> loadFromImage(const Image& image, bool sRgb = false, const IntRect& area = {});
[[nodiscard]] static std::optional<Texture> createFromImage(const Image& image, bool sRgb = false, const IntRect& area = {});
////////////////////////////////////////////////////////////
/// \brief Return the size of the texture
@ -226,7 +375,7 @@ public:
///
/// \return Image containing the texture's pixels
///
/// \see loadFromImage
/// \see createFromImage
///
////////////////////////////////////////////////////////////
[[nodiscard]] Image copyToImage() const;
@ -545,14 +694,6 @@ private:
friend class RenderTexture;
friend class RenderTarget;
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Creates an empty texture.
///
////////////////////////////////////////////////////////////
Texture(Vector2u size, Vector2u actualSize, unsigned int texture, bool sRgb);
////////////////////////////////////////////////////////////
/// \brief Get a valid image size according to hardware support
///
@ -630,7 +771,7 @@ SFML_GRAPHICS_API void swap(Texture& left, Texture& right) noexcept;
/// However, if you want to perform some modifications on the pixels
/// before creating the final texture, you can load your file to a
/// sf::Image, do whatever you need with the pixels, and then call
/// Texture::loadFromImage.
/// Texture::createFromImage.
///
/// 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
@ -662,7 +803,7 @@ SFML_GRAPHICS_API void swap(Texture& left, Texture& right) noexcept;
/// // drawing a sprite
///
/// // Load a texture from a file
/// const auto texture = sf::Texture::loadFromFile("texture.png").value();
/// const auto texture = sf::Texture::createFromFile("texture.png").value();
///
/// // Assign it to a sprite
/// sf::Sprite sprite(texture);

View File

@ -56,6 +56,15 @@ namespace sf
class SFML_SYSTEM_API FileInputStream : public InputStream
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Construct a file input stream that is not associated
/// with a file to read.
///
////////////////////////////////////////////////////////////
FileInputStream();
////////////////////////////////////////////////////////////
/// \brief Default destructor
///
@ -91,10 +100,20 @@ public:
///
/// \param filename Name of the file to open
///
/// \return True on success, false on error
///
////////////////////////////////////////////////////////////
[[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> open(const std::filesystem::path& filename);
[[nodiscard]] static std::optional<FileInputStream> create(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Read data from the stream
@ -146,20 +165,6 @@ private:
void operator()(std::FILE* file);
};
////////////////////////////////////////////////////////////
/// \brief Construct from file
///
////////////////////////////////////////////////////////////
explicit FileInputStream(std::unique_ptr<std::FILE, FileCloser>&& file);
#ifdef SFML_SYSTEM_ANDROID
////////////////////////////////////////////////////////////
/// \brief Construct from resource stream
///
////////////////////////////////////////////////////////////
explicit FileInputStream(std::unique_ptr<priv::ResourceStream>&& androidFile);
#endif
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////

View File

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

View File

@ -55,6 +55,15 @@ public:
////////////////////////////////////////////////////////////
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
///

View File

@ -174,7 +174,7 @@ public:
/// `std::nullopt` otherwise
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Cursor> loadFromPixels(const std::uint8_t* pixels, Vector2u size, Vector2u hotspot);
[[nodiscard]] static std::optional<Cursor> createFromPixels(const std::uint8_t* pixels, Vector2u size, Vector2u hotspot);
////////////////////////////////////////////////////////////
/// \brief Create a native system cursor
@ -190,7 +190,7 @@ public:
/// `std::nullopt` otherwise
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<Cursor> loadFromSystem(Type type);
[[nodiscard]] static std::optional<Cursor> createFromSystem(Type type);
private:
friend class WindowBase;
@ -232,8 +232,8 @@ private:
/// associated with either a native system cursor or a custom
/// cursor.
///
/// After loading the cursor the graphical appearance
/// with either loadFromPixels() or loadFromSystem(), the
/// After loading the cursor graphical appearance
/// with either createFromPixels() or createFromSystem(), the
/// cursor can be changed with sf::WindowBase::setMouseCursor().
///
/// The behavior is undefined if the cursor is destroyed while
@ -245,7 +245,7 @@ private:
///
/// // ... create window as usual ...
///
/// const auto cursor = sf::Cursor::loadFromSystem(sf::Cursor::Type::Hand).value();
/// const auto cursor = sf::Cursor::createFromSystem(sf::Cursor::Type::Hand).value();
/// window.setMouseCursor(cursor);
/// \endcode
///

View File

@ -456,8 +456,8 @@ public:
///
/// \param cursor Native system cursor type to display
///
/// \see sf::Cursor::loadFromSystem
/// \see sf::Cursor::loadFromPixels
/// \see sf::Cursor::createFromSystem
/// \see sf::Cursor::createFromPixels
///
////////////////////////////////////////////////////////////
void setMouseCursor(const Cursor& cursor);

View File

@ -67,96 +67,164 @@ void InputSoundFile::StreamDeleter::operator()(InputStream* ptr) const
////////////////////////////////////////////////////////////
std::optional<InputSoundFile> InputSoundFile::openFromFile(const std::filesystem::path& filename)
bool InputSoundFile::openFromFile(const std::filesystem::path& filename)
{
// If the file is already open, first close it
close();
// Find a suitable reader for the file type
auto reader = SoundFileFactory::createReaderFromFilename(filename);
if (!reader)
{
// Error message generated in called function.
return std::nullopt;
}
return false;
// Open the file
auto fileInputStream = FileInputStream::open(filename);
if (!fileInputStream)
// Wrap the file into a stream
auto file = std::make_unique<FileInputStream>();
// Open it
if (!file->open(filename))
{
err() << "Failed to open input sound file from file (couldn't open file input stream)\n"
<< formatDebugPathInfo(filename) << std::endl;
return std::nullopt;
return false;
}
// Wrap the file into a stream
auto file = std::make_unique<FileInputStream>(std::move(*fileInputStream));
// Pass the stream to the reader
auto info = reader->open(*file);
const auto info = reader->open(*file);
if (!info)
{
err() << "Failed to open input sound file from file (reader open failure)\n"
<< formatDebugPathInfo(filename) << std::endl;
return std::nullopt;
return false;
}
return InputSoundFile(std::move(reader), std::move(file), info->sampleCount, info->sampleRate, std::move(info->channelMap));
// Take ownership of successfully opened reader and stream
m_reader = std::move(reader);
m_stream = std::move(file);
// Retrieve the attributes of the open sound file
m_sampleCount = info->sampleCount;
m_sampleRate = info->sampleRate;
m_channelMap = info->channelMap;
return true;
}
////////////////////////////////////////////////////////////
std::optional<InputSoundFile> InputSoundFile::openFromMemory(const void* data, std::size_t sizeInBytes)
bool InputSoundFile::openFromMemory(const void* data, std::size_t sizeInBytes)
{
// If the file is already open, first close it
close();
// Find a suitable reader for the file type
auto reader = SoundFileFactory::createReaderFromMemory(data, sizeInBytes);
if (!reader)
{
// Error message generated in called function.
return std::nullopt;
return false;
}
// Wrap the memory file into a stream
auto memory = std::make_unique<MemoryInputStream>(data, sizeInBytes);
// Pass the stream to the reader
auto info = reader->open(*memory);
const auto info = reader->open(*memory);
if (!info)
{
err() << "Failed to open input sound file from memory (reader open failure)" << std::endl;
return std::nullopt;
return false;
}
return InputSoundFile(std::move(reader), std::move(memory), info->sampleCount, info->sampleRate, std::move(info->channelMap));
// Take ownership of successfully opened reader and stream
m_reader = std::move(reader);
m_stream = std::move(memory);
// Retrieve the attributes of the open sound file
m_sampleCount = info->sampleCount;
m_sampleRate = info->sampleRate;
m_channelMap = info->channelMap;
return true;
}
////////////////////////////////////////////////////////////
std::optional<InputSoundFile> InputSoundFile::openFromStream(InputStream& stream)
bool InputSoundFile::openFromStream(InputStream& stream)
{
// If the file is already open, first close it
close();
// Find a suitable reader for the file type
auto reader = SoundFileFactory::createReaderFromStream(stream);
if (!reader)
{
// Error message generated in called function.
return std::nullopt;
return false;
}
// Don't forget to reset the stream to its beginning before re-opening it
if (stream.seek(0) != 0)
{
err() << "Failed to open sound file from stream (cannot restart stream)" << std::endl;
return std::nullopt;
return false;
}
// Pass the stream to the reader
auto info = reader->open(stream);
const auto info = reader->open(stream);
if (!info)
{
err() << "Failed to open input sound file from stream (reader open failure)" << std::endl;
return std::nullopt;
return false;
}
return InputSoundFile(std::move(reader), {&stream, false}, info->sampleCount, info->sampleRate, std::move(info->channelMap));
// Take ownership of reader and store a reference to the stream without taking ownership
m_reader = std::move(reader);
m_stream = {&stream, false};
// Retrieve the attributes of the open sound file
m_sampleCount = info->sampleCount;
m_sampleRate = info->sampleRate;
m_channelMap = info->channelMap;
return true;
}
////////////////////////////////////////////////////////////
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;
}
@ -237,7 +305,7 @@ void InputSoundFile::seek(std::uint64_t sampleOffset)
////////////////////////////////////////////////////////////
void InputSoundFile::seek(Time timeOffset)
{
seek(static_cast<std::size_t>(timeOffset.asSeconds() * static_cast<float>(m_sampleRate)) * m_channelMap.size());
seek(static_cast<std::uint64_t>(timeOffset.asSeconds() * static_cast<float>(m_sampleRate)) * m_channelMap.size());
}
@ -260,19 +328,4 @@ void InputSoundFile::close()
*this = {};
}
////////////////////////////////////////////////////////////
InputSoundFile::InputSoundFile(std::unique_ptr<SoundFileReader>&& reader,
std::unique_ptr<InputStream, StreamDeleter>&& stream,
std::uint64_t sampleCount,
unsigned int sampleRate,
std::vector<SoundChannel>&& channelMap) :
m_reader(std::move(reader)),
m_stream(std::move(stream)),
m_sampleCount(sampleCount),
m_sampleRate(sampleRate),
m_channelMap(std::move(channelMap))
{
}
} // namespace sf

View File

@ -46,19 +46,24 @@ struct Music::Impl
std::recursive_mutex mutex; //!< Mutex protecting the data
Span<std::uint64_t> loopSpan; //!< Loop Range Specifier
explicit Impl(InputSoundFile&& theFile) :
file(std::move(theFile)),
void initialize()
{
// Compute the music positions
loopSpan.offset = 0;
loopSpan.length = file.getSampleCount();
// Resize the internal buffer so that it can contain 1 second of audio samples
samples(file.getSampleRate() * file.getChannelCount()),
// Compute the music positions
loopSpan{0u, file.getSampleCount()}
{
samples.resize(file.getSampleRate() * file.getChannelCount());
}
};
////////////////////////////////////////////////////////////
Music::Music() : m_impl(std::make_unique<Impl>())
{
}
////////////////////////////////////////////////////////////
Music::~Music()
{
@ -79,37 +84,107 @@ Music& Music::operator=(Music&&) noexcept = default;
////////////////////////////////////////////////////////////
std::optional<Music> Music::tryOpenFromInputSoundFile(std::optional<InputSoundFile>&& optFile, const char* errorContext)
bool Music::openFromFile(const std::filesystem::path& filename)
{
if (!optFile.has_value())
// First stop the music if it was already running
stop();
// Open the underlying sound file
if (!m_impl->file.openFromFile(filename))
{
err() << "Failed to open music from " << errorContext << std::endl;
return std::nullopt;
err() << "Failed to open music from file" << std::endl;
return false;
}
// TODO: apply RVO here via passkey idiom
return Music(std::move(*optFile));
// Perform common initializations
m_impl->initialize();
// Initialize the stream
SoundStream::initialize(m_impl->file.getChannelCount(), m_impl->file.getSampleRate(), m_impl->file.getChannelMap());
return true;
}
////////////////////////////////////////////////////////////
std::optional<Music> Music::openFromFile(const std::filesystem::path& filename)
bool Music::openFromMemory(const void* data, std::size_t sizeInBytes)
{
return tryOpenFromInputSoundFile(InputSoundFile::openFromFile(filename), "file");
// First stop the music if it was already running
stop();
// Open the underlying sound file
if (!m_impl->file.openFromMemory(data, sizeInBytes))
{
err() << "Failed to open music from memory" << std::endl;
return false;
}
// Perform common initializations
m_impl->initialize();
// Initialize the stream
SoundStream::initialize(m_impl->file.getChannelCount(), m_impl->file.getSampleRate(), m_impl->file.getChannelMap());
return true;
}
////////////////////////////////////////////////////////////
std::optional<Music> Music::openFromMemory(const void* data, std::size_t sizeInBytes)
bool Music::openFromStream(InputStream& stream)
{
return tryOpenFromInputSoundFile(InputSoundFile::openFromMemory(data, sizeInBytes), "memory");
// First stop the music if it was already running
stop();
// Open the underlying sound file
if (!m_impl->file.openFromStream(stream))
{
err() << "Failed to open music from stream" << std::endl;
return false;
}
// Perform common initializations
m_impl->initialize();
// Initialize the stream
SoundStream::initialize(m_impl->file.getChannelCount(), m_impl->file.getSampleRate(), m_impl->file.getChannelMap());
return true;
}
////////////////////////////////////////////////////////////
std::optional<Music> Music::openFromStream(InputStream& stream)
std::optional<Music> Music::createFromFile(const std::filesystem::path& filename)
{
return tryOpenFromInputSoundFile(InputSoundFile::openFromStream(stream), "stream");
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;
}
@ -247,14 +322,6 @@ std::optional<std::uint64_t> Music::onLoop()
}
////////////////////////////////////////////////////////////
Music::Music(InputSoundFile&& file) : m_impl(std::make_unique<Impl>(std::move(file)))
{
// Initialize the stream
SoundStream::initialize(m_impl->file.getChannelCount(), m_impl->file.getSampleRate(), m_impl->file.getChannelMap());
}
////////////////////////////////////////////////////////////
std::uint64_t Music::timeToSamples(Time position) const
{

View File

@ -37,28 +37,47 @@
namespace sf
{
////////////////////////////////////////////////////////////
std::optional<OutputSoundFile> OutputSoundFile::openFromFile(
bool OutputSoundFile::openFromFile(const std::filesystem::path& filename,
unsigned int sampleRate,
unsigned int channelCount,
const std::vector<SoundChannel>& channelMap)
{
// If the file is already open, first close it
close();
// Find a suitable writer for the file type
m_writer = SoundFileFactory::createWriterFromFilename(filename);
if (!m_writer)
{
// Error message generated in called function.
return false;
}
// Pass the stream to the reader
if (!m_writer->open(filename, sampleRate, channelCount, channelMap))
{
err() << "Failed to open output sound file from file (writer open failure)" << std::endl;
close();
return false;
}
return true;
}
////////////////////////////////////////////////////////////
std::optional<OutputSoundFile> OutputSoundFile::createFromFile(
const std::filesystem::path& filename,
unsigned int sampleRate,
unsigned int channelCount,
const std::vector<SoundChannel>& channelMap)
{
// Find a suitable writer for the file type
auto writer = SoundFileFactory::createWriterFromFilename(filename);
if (!writer)
{
// Error message generated in called function.
return std::nullopt;
}
auto outputSoundFile = std::make_optional<OutputSoundFile>();
// Pass the stream to the reader
if (!writer->open(filename, sampleRate, channelCount, channelMap))
{
err() << "Failed to open output sound file from file (writer open failure)" << std::endl;
if (!outputSoundFile->openFromFile(filename, sampleRate, channelCount, channelMap))
return std::nullopt;
}
return OutputSoundFile(std::move(writer));
return outputSoundFile;
}
@ -79,10 +98,4 @@ void OutputSoundFile::close()
m_writer.reset();
}
////////////////////////////////////////////////////////////
OutputSoundFile::OutputSoundFile(std::unique_ptr<SoundFileWriter>&& writer) : m_writer(std::move(writer))
{
}
} // namespace sf

View File

@ -68,41 +68,43 @@ SoundBuffer::~SoundBuffer()
////////////////////////////////////////////////////////////
std::optional<SoundBuffer> SoundBuffer::loadFromFile(const std::filesystem::path& filename)
bool SoundBuffer::loadFromFile(const std::filesystem::path& filename)
{
if (auto file = InputSoundFile::openFromFile(filename))
return initialize(*file);
InputSoundFile file;
if (file.openFromFile(filename))
return initialize(file);
err() << "Failed to open sound buffer from file" << std::endl;
return std::nullopt;
return false;
}
////////////////////////////////////////////////////////////
std::optional<SoundBuffer> SoundBuffer::loadFromMemory(const void* data, std::size_t sizeInBytes)
bool SoundBuffer::loadFromMemory(const void* data, std::size_t sizeInBytes)
{
if (auto file = InputSoundFile::openFromMemory(data, sizeInBytes))
return initialize(*file);
InputSoundFile file;
if (file.openFromMemory(data, sizeInBytes))
return initialize(file);
err() << "Failed to open sound buffer from memory" << std::endl;
return std::nullopt;
return false;
}
////////////////////////////////////////////////////////////
std::optional<SoundBuffer> SoundBuffer::loadFromStream(InputStream& stream)
bool SoundBuffer::loadFromStream(InputStream& stream)
{
if (auto file = InputSoundFile::openFromStream(stream))
return initialize(*file);
InputSoundFile file;
if (file.openFromStream(stream))
return initialize(file);
err() << "Failed to open sound buffer from stream" << std::endl;
return std::nullopt;
return false;
}
////////////////////////////////////////////////////////////
std::optional<SoundBuffer> SoundBuffer::loadFromSamples(
const std::int16_t* samples,
bool SoundBuffer::loadFromSamples(const std::int16_t* samples,
std::uint64_t sampleCount,
unsigned int channelCount,
unsigned int sampleRate,
@ -111,12 +113,10 @@ std::optional<SoundBuffer> SoundBuffer::loadFromSamples(
if (samples && sampleCount && channelCount && sampleRate && !channelMap.empty())
{
// Copy the new audio samples
SoundBuffer soundBuffer(std::vector<std::int16_t>(samples, samples + sampleCount));
m_samples.assign(samples, samples + sampleCount);
// Update the internal buffer with the new samples
if (!soundBuffer.update(channelCount, sampleRate, channelMap))
return std::nullopt;
return soundBuffer;
return update(channelCount, sampleRate, channelMap);
}
// Error...
@ -126,7 +126,60 @@ std::optional<SoundBuffer> SoundBuffer::loadFromSamples(
<< "channels: " << channelCount << ", "
<< "samplerate: " << sampleRate << ")" << std::endl;
return false;
}
////////////////////////////////////////////////////////////
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;
}
@ -134,10 +187,11 @@ std::optional<SoundBuffer> SoundBuffer::loadFromSamples(
bool SoundBuffer::saveToFile(const std::filesystem::path& filename) const
{
// Create the sound file in write mode
if (auto file = OutputSoundFile::openFromFile(filename, getSampleRate(), getChannelCount(), getChannelMap()))
OutputSoundFile file;
if (file.openFromFile(filename, getSampleRate(), getChannelCount(), getChannelMap()))
{
// Write the samples to the opened file
file->write(m_samples.data(), m_samples.size());
file.write(m_samples.data(), m_samples.size());
return true;
}
@ -204,33 +258,26 @@ SoundBuffer& SoundBuffer::operator=(const SoundBuffer& right)
////////////////////////////////////////////////////////////
SoundBuffer::SoundBuffer(std::vector<std::int16_t>&& samples) : m_samples(std::move(samples))
{
}
////////////////////////////////////////////////////////////
std::optional<SoundBuffer> SoundBuffer::initialize(InputSoundFile& file)
bool SoundBuffer::initialize(InputSoundFile& file)
{
// Retrieve the sound parameters
const std::uint64_t sampleCount = file.getSampleCount();
// Read the samples from the provided file
std::vector<std::int16_t> samples(static_cast<std::size_t>(sampleCount));
if (file.read(samples.data(), sampleCount) == sampleCount)
m_samples.resize(static_cast<std::size_t>(sampleCount));
if (file.read(m_samples.data(), sampleCount) == sampleCount)
{
// Update the internal buffer with the new samples
SoundBuffer soundBuffer(std::move(samples));
if (!soundBuffer.update(file.getChannelCount(), file.getSampleRate(), file.getChannelMap()))
if (!update(file.getChannelCount(), file.getSampleRate(), file.getChannelMap()))
{
err() << "Failed to initialize sound buffer (internal update failure)" << std::endl;
return std::nullopt;
return false;
}
return soundBuffer;
return true;
}
return std::nullopt;
return false;
}

View File

@ -48,7 +48,7 @@ SoundBufferRecorder::~SoundBufferRecorder()
bool SoundBufferRecorder::onStart()
{
m_samples.clear();
m_buffer.reset();
m_buffer = SoundBuffer();
return true;
}
@ -69,12 +69,7 @@ void SoundBufferRecorder::onStop()
if (m_samples.empty())
return;
m_buffer = sf::SoundBuffer::loadFromSamples(m_samples.data(),
m_samples.size(),
getChannelCount(),
getSampleRate(),
getChannelMap());
if (!m_buffer)
if (!m_buffer.loadFromSamples(m_samples.data(), m_samples.size(), getChannelCount(), getSampleRate(), getChannelMap()))
err() << "Failed to stop capturing audio data" << std::endl;
}
@ -82,8 +77,7 @@ void SoundBufferRecorder::onStop()
////////////////////////////////////////////////////////////
const SoundBuffer& SoundBufferRecorder::getBuffer() const
{
assert(m_buffer && "SoundBufferRecorder::getBuffer() Cannot return reference to null buffer");
return *m_buffer;
return m_buffer;
}
} // namespace sf

View File

@ -48,8 +48,8 @@ namespace sf
std::unique_ptr<SoundFileReader> SoundFileFactory::createReaderFromFilename(const std::filesystem::path& filename)
{
// Wrap the input file into a file stream
auto stream = FileInputStream::open(filename);
if (!stream)
FileInputStream stream;
if (!stream.open(filename))
{
err() << "Failed to open sound file (couldn't open stream)\n" << formatDebugPathInfo(filename) << std::endl;
return nullptr;
@ -58,13 +58,13 @@ std::unique_ptr<SoundFileReader> SoundFileFactory::createReaderFromFilename(cons
// Test the filename in all the registered factories
for (const auto& [fpCreate, fpCheck] : getReaderFactoryMap())
{
if (!stream->seek(0).has_value())
if (!stream.seek(0).has_value())
{
err() << "Failed to seek sound stream" << std::endl;
return nullptr;
}
if (fpCheck(*stream))
if (fpCheck(stream))
return fpCreate();
}

View File

@ -45,7 +45,6 @@
#include <ostream>
#include <utility>
#include <cassert>
#include <cmath>
#include <cstring>
@ -122,17 +121,13 @@ struct Font::FontHandles
////////////////////////////////////////////////////////////
Font::Font(std::shared_ptr<FontHandles>&& fontHandles, std::string&& familyName) : m_fontHandles(std::move(fontHandles))
{
m_info.family = std::move(familyName);
}
////////////////////////////////////////////////////////////
std::optional<Font> Font::openFromFile(const std::filesystem::path& filename)
bool Font::openFromFile(const std::filesystem::path& filename)
{
#ifndef SFML_SYSTEM_ANDROID
// Cleanup the previous resources
cleanup();
auto fontHandles = std::make_shared<FontHandles>();
// Initialize FreeType
@ -141,7 +136,7 @@ std::optional<Font> Font::openFromFile(const std::filesystem::path& filename)
if (FT_Init_FreeType(&fontHandles->library) != 0)
{
err() << "Failed to load font (failed to initialize FreeType)\n" << formatDebugPathInfo(filename) << std::endl;
return std::nullopt;
return false;
}
// Load the new font face from the specified file
@ -149,7 +144,7 @@ std::optional<Font> Font::openFromFile(const std::filesystem::path& filename)
if (FT_New_Face(fontHandles->library, filename.string().c_str(), 0, &face) != 0)
{
err() << "Failed to load font (failed to create the font face)\n" << formatDebugPathInfo(filename) << std::endl;
return std::nullopt;
return false;
}
fontHandles->face = face;
@ -157,7 +152,7 @@ std::optional<Font> Font::openFromFile(const std::filesystem::path& filename)
if (FT_Stroker_New(fontHandles->library, &fontHandles->stroker) != 0)
{
err() << "Failed to load font (failed to create the stroker)\n" << formatDebugPathInfo(filename) << std::endl;
return std::nullopt;
return false;
}
// Select the unicode character map
@ -165,26 +160,32 @@ std::optional<Font> Font::openFromFile(const std::filesystem::path& filename)
{
err() << "Failed to load font (failed to set the Unicode character set)\n"
<< formatDebugPathInfo(filename) << std::endl;
return std::nullopt;
return false;
}
return Font(std::move(fontHandles), std::string(face->family_name ? face->family_name : ""));
// Store the loaded font handles
m_fontHandles = std::move(fontHandles);
// Store the font information
m_info.family = face->family_name ? face->family_name : std::string();
return true;
#else
auto stream = std::make_shared<priv::ResourceStream>(filename);
auto font = openFromStream(*stream);
if (font)
font->m_stream = std::move(stream);
return font;
m_stream = std::make_shared<priv::ResourceStream>(filename);
return openFromStream(*m_stream);
#endif
}
////////////////////////////////////////////////////////////
std::optional<Font> Font::openFromMemory(const void* data, std::size_t sizeInBytes)
bool Font::openFromMemory(const void* data, std::size_t sizeInBytes)
{
// Cleanup the previous resources
cleanup();
auto fontHandles = std::make_shared<FontHandles>();
// Initialize FreeType
@ -193,7 +194,7 @@ std::optional<Font> Font::openFromMemory(const void* data, std::size_t sizeInByt
if (FT_Init_FreeType(&fontHandles->library) != 0)
{
err() << "Failed to load font from memory (failed to initialize FreeType)" << std::endl;
return std::nullopt;
return false;
}
// Load the new font face from the specified file
@ -205,7 +206,7 @@ std::optional<Font> Font::openFromMemory(const void* data, std::size_t sizeInByt
&face) != 0)
{
err() << "Failed to load font from memory (failed to create the font face)" << std::endl;
return std::nullopt;
return false;
}
fontHandles->face = face;
@ -213,23 +214,32 @@ std::optional<Font> Font::openFromMemory(const void* data, std::size_t sizeInByt
if (FT_Stroker_New(fontHandles->library, &fontHandles->stroker) != 0)
{
err() << "Failed to load font from memory (failed to create the stroker)" << std::endl;
return std::nullopt;
return false;
}
// Select the Unicode character map
if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0)
{
err() << "Failed to load font from memory (failed to set the Unicode character set)" << std::endl;
return std::nullopt;
return false;
}
return Font(std::move(fontHandles), std::string(face->family_name ? face->family_name : ""));
// Store the loaded font handles
m_fontHandles = std::move(fontHandles);
// Store the font information
m_info.family = face->family_name ? face->family_name : std::string();
return true;
}
////////////////////////////////////////////////////////////
std::optional<Font> Font::openFromStream(InputStream& stream)
bool Font::openFromStream(InputStream& stream)
{
// Cleanup the previous resources
cleanup();
auto fontHandles = std::make_shared<FontHandles>();
// Initialize FreeType
@ -238,14 +248,14 @@ std::optional<Font> Font::openFromStream(InputStream& stream)
if (FT_Init_FreeType(&fontHandles->library) != 0)
{
err() << "Failed to load font from stream (failed to initialize FreeType)" << std::endl;
return std::nullopt;
return false;
}
// Make sure that the stream's reading position is at the beginning
if (!stream.seek(0).has_value())
if (!stream.seek(0))
{
err() << "Failed to seek font stream" << std::endl;
return std::nullopt;
return false;
}
// Prepare a wrapper for our stream, that we'll pass to FreeType callbacks
@ -267,7 +277,7 @@ std::optional<Font> Font::openFromStream(InputStream& stream)
if (FT_Open_Face(fontHandles->library, &args, 0, &face) != 0)
{
err() << "Failed to load font from stream (failed to create the font face)" << std::endl;
return std::nullopt;
return false;
}
fontHandles->face = face;
@ -275,17 +285,59 @@ std::optional<Font> Font::openFromStream(InputStream& stream)
if (FT_Stroker_New(fontHandles->library, &fontHandles->stroker) != 0)
{
err() << "Failed to load font from stream (failed to create the stroker)" << std::endl;
return std::nullopt;
return false;
}
// Select the Unicode character map
if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0)
{
err() << "Failed to load font from stream (failed to set the Unicode character set)" << std::endl;
return std::nullopt;
return false;
}
return Font(std::move(fontHandles), std::string(face->family_name ? face->family_name : ""));
// Store the loaded font handles
m_fontHandles = std::move(fontHandles);
// Store the font information
m_info.family = face->family_name ? face->family_name : std::string();
return true;
}
////////////////////////////////////////////////////////////
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;
}
@ -299,13 +351,13 @@ const Font::Info& Font::getInfo() const
////////////////////////////////////////////////////////////
const Glyph& Font::getGlyph(std::uint32_t codePoint, unsigned int characterSize, bool bold, float outlineThickness) const
{
assert(m_fontHandles);
// Get the page corresponding to the character size
GlyphTable& glyphs = loadPage(characterSize).glyphs;
// Build the key by combining the glyph index (based on code point), bold flag, and outline thickness
const std::uint64_t key = combine(outlineThickness, bold, FT_Get_Char_Index(m_fontHandles->face, codePoint));
const std::uint64_t key = combine(outlineThickness,
bold,
FT_Get_Char_Index(m_fontHandles ? m_fontHandles->face : nullptr, codePoint));
// Search the glyph into the cache
if (const auto it = glyphs.find(key); it != glyphs.end())
@ -323,21 +375,18 @@ const Glyph& Font::getGlyph(std::uint32_t codePoint, unsigned int characterSize,
////////////////////////////////////////////////////////////
bool Font::hasGlyph(std::uint32_t codePoint) const
{
assert(m_fontHandles);
return FT_Get_Char_Index(m_fontHandles->face, codePoint) != 0;
return FT_Get_Char_Index(m_fontHandles ? m_fontHandles->face : nullptr, codePoint) != 0;
}
////////////////////////////////////////////////////////////
float Font::getKerning(std::uint32_t first, std::uint32_t second, unsigned int characterSize, bool bold) const
{
assert(m_fontHandles);
// Special case where first or second is 0 (null character)
if (first == 0 || second == 0)
return 0.f;
FT_Face face = m_fontHandles->face;
FT_Face face = m_fontHandles ? m_fontHandles->face : nullptr;
if (face && setCurrentSize(characterSize))
{
@ -360,7 +409,8 @@ float Font::getKerning(std::uint32_t first, std::uint32_t second, unsigned int c
// 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
return std::floor((secondLsbDelta - firstRsbDelta + static_cast<float>(kerning.x) + 32) / float{1 << 6});
return std::floor(
(secondLsbDelta - firstRsbDelta + static_cast<float>(kerning.x) + 32) / static_cast<float>(1 << 6));
}
// Invalid font
@ -371,13 +421,11 @@ float Font::getKerning(std::uint32_t first, std::uint32_t second, unsigned int c
////////////////////////////////////////////////////////////
float Font::getLineSpacing(unsigned int characterSize) const
{
assert(m_fontHandles);
FT_Face face = m_fontHandles ? m_fontHandles->face : nullptr;
FT_Face face = m_fontHandles->face;
if (setCurrentSize(characterSize))
if (face && setCurrentSize(characterSize))
{
return static_cast<float>(face->size->metrics.height) / float{1 << 6};
return static_cast<float>(face->size->metrics.height) / static_cast<float>(1 << 6);
}
return 0.f;
@ -387,17 +435,16 @@ float Font::getLineSpacing(unsigned int characterSize) const
////////////////////////////////////////////////////////////
float Font::getUnderlinePosition(unsigned int characterSize) const
{
assert(m_fontHandles);
FT_Face face = m_fontHandles ? m_fontHandles->face : nullptr;
FT_Face face = m_fontHandles->face;
if (setCurrentSize(characterSize))
if (face && setCurrentSize(characterSize))
{
// Return a fixed position if font is a bitmap font
if (!FT_IS_SCALABLE(face))
return static_cast<float>(characterSize) / 10.f;
return -static_cast<float>(FT_MulFix(face->underline_position, face->size->metrics.y_scale)) / float{1 << 6};
return -static_cast<float>(FT_MulFix(face->underline_position, face->size->metrics.y_scale)) /
static_cast<float>(1 << 6);
}
return 0.f;
@ -407,9 +454,7 @@ float Font::getUnderlinePosition(unsigned int characterSize) const
////////////////////////////////////////////////////////////
float Font::getUnderlineThickness(unsigned int characterSize) const
{
assert(m_fontHandles);
FT_Face face = m_fontHandles->face;
FT_Face face = m_fontHandles ? m_fontHandles->face : nullptr;
if (face && setCurrentSize(characterSize))
{
@ -417,7 +462,8 @@ float Font::getUnderlineThickness(unsigned int characterSize) const
if (!FT_IS_SCALABLE(face))
return static_cast<float>(characterSize) / 14.f;
return static_cast<float>(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale)) / float{1 << 6};
return static_cast<float>(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale)) /
static_cast<float>(1 << 6);
}
return 0.f;
@ -451,15 +497,22 @@ bool Font::isSmooth() const
}
////////////////////////////////////////////////////////////
void Font::cleanup()
{
// Drop ownership of shared FreeType pointers
m_fontHandles.reset();
// Reset members
m_pages.clear();
std::vector<std::uint8_t>().swap(m_pixelBuffer);
}
////////////////////////////////////////////////////////////
Font::Page& Font::loadPage(unsigned int characterSize) const
{
if (const auto it = m_pages.find(characterSize); it != m_pages.end())
return it->second;
auto page = Page::create(m_isSmooth);
assert(page && "Font::loadPage() Failed to load page");
return m_pages.emplace(characterSize, std::move(*page)).first->second;
return m_pages.try_emplace(characterSize, m_isSmooth).first->second;
}
@ -469,6 +522,10 @@ Glyph Font::loadGlyph(std::uint32_t codePoint, unsigned int characterSize, bool
// The glyph to return
Glyph glyph;
// Stop if no font is loaded
if (!m_fontHandles)
return glyph;
// Get our FT_Face
FT_Face face = m_fontHandles->face;
if (!face)
@ -506,7 +563,7 @@ Glyph Font::loadGlyph(std::uint32_t codePoint, unsigned int characterSize, bool
FT_Stroker stroker = m_fontHandles->stroker;
FT_Stroker_Set(stroker,
static_cast<FT_Fixed>(outlineThickness * float{1 << 6}),
static_cast<FT_Fixed>(outlineThickness * static_cast<float>(1 << 6)),
FT_STROKER_LINECAP_ROUND,
FT_STROKER_LINEJOIN_ROUND,
0);
@ -534,41 +591,47 @@ Glyph Font::loadGlyph(std::uint32_t codePoint, unsigned int characterSize, bool
// Compute the glyph's advance offset
glyph.advance = static_cast<float>(bitmapGlyph->root.advance.x >> 16);
if (bold)
glyph.advance += static_cast<float>(weight) / float{1 << 6};
glyph.advance += static_cast<float>(weight) / static_cast<float>(1 << 6);
glyph.lsbDelta = static_cast<int>(face->glyph->lsb_delta);
glyph.rsbDelta = static_cast<int>(face->glyph->rsb_delta);
Vector2u size(bitmap.width, bitmap.rows);
unsigned int width = bitmap.width;
unsigned int height = bitmap.rows;
if ((size.x > 0) && (size.y > 0))
if ((width > 0) && (height > 0))
{
// Leave a small padding around characters, so that filtering doesn't
// pollute them with pixels from neighbors
const unsigned int padding = 2;
size += 2u * Vector2u(padding, padding);
width += 2 * padding;
height += 2 * padding;
// Get the glyphs page corresponding to the character size
Page& page = loadPage(characterSize);
// Find a good position for the new glyph into the texture
glyph.textureRect = findGlyphRect(page, size);
glyph.textureRect = findGlyphRect(page, {width, height});
// Make sure the texture data is positioned in the center
// of the allocated texture rectangle
glyph.textureRect.position += Vector2i(padding, padding);
glyph.textureRect.size -= 2 * Vector2i(padding, padding);
glyph.textureRect.position.x += static_cast<int>(padding);
glyph.textureRect.position.y += static_cast<int>(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
glyph.bounds.position = Vector2f(Vector2i(bitmapGlyph->left, -bitmapGlyph->top));
glyph.bounds.size = Vector2f(Vector2u(bitmap.width, bitmap.rows));
glyph.bounds.position.x = static_cast<float>(bitmapGlyph->left);
glyph.bounds.position.y = static_cast<float>(-bitmapGlyph->top);
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
m_pixelBuffer.resize(static_cast<std::size_t>(size.x) * static_cast<std::size_t>(size.y) * 4);
m_pixelBuffer.resize(static_cast<std::size_t>(width) * static_cast<std::size_t>(height) * 4);
std::uint8_t* current = m_pixelBuffer.data();
std::uint8_t* end = current + size.x * size.y * 4;
std::uint8_t* end = current + width * height * 4;
while (current != end)
{
@ -583,12 +646,12 @@ Glyph Font::loadGlyph(std::uint32_t codePoint, unsigned int characterSize, bool
if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
{
// Pixels are 1 bit monochrome values
for (unsigned int y = padding; y < size.y - padding; ++y)
for (unsigned int y = padding; y < height - padding; ++y)
{
for (unsigned int x = padding; x < size.x - padding; ++x)
for (unsigned int x = padding; x < width - padding; ++x)
{
// The color channels remain white, just fill the alpha channel
const std::size_t index = x + y * size.x;
const std::size_t index = x + y * width;
m_pixelBuffer[index * 4 + 3] = ((pixels[(x - padding) / 8]) & (1 << (7 - ((x - padding) % 8)))) ? 255 : 0;
}
pixels += bitmap.pitch;
@ -596,13 +659,13 @@ Glyph Font::loadGlyph(std::uint32_t codePoint, unsigned int characterSize, bool
}
else
{
// Pixels are 8 bit gray levels
for (unsigned int y = padding; y < size.y - padding; ++y)
// Pixels are 8 bits gray levels
for (unsigned int y = padding; y < height - padding; ++y)
{
for (unsigned int x = padding; x < size.x - padding; ++x)
for (unsigned int x = padding; x < width - padding; ++x)
{
// The color channels remain white, just fill the alpha channel
const std::size_t index = x + y * size.x;
const std::size_t index = x + y * width;
m_pixelBuffer[index * 4 + 3] = pixels[x - padding];
}
pixels += bitmap.pitch;
@ -610,9 +673,11 @@ Glyph Font::loadGlyph(std::uint32_t codePoint, unsigned int characterSize, bool
}
// Write the pixels to the texture
const auto dest = Vector2u(glyph.textureRect.position) - Vector2u(padding, padding);
const auto updateSize = Vector2u(glyph.textureRect.size) + 2u * Vector2u(padding, padding);
page.texture.update(m_pixelBuffer.data(), updateSize, dest);
const unsigned int x = static_cast<unsigned int>(glyph.textureRect.position.x) - padding;
const unsigned int y = static_cast<unsigned int>(glyph.textureRect.position.y) - padding;
const unsigned int w = static_cast<unsigned int>(glyph.textureRect.size.x) + 2 * padding;
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
@ -661,16 +726,16 @@ IntRect Font::findGlyphRect(Page& page, Vector2u size) const
if ((textureSize.x * 2 <= Texture::getMaximumSize()) && (textureSize.y * 2 <= Texture::getMaximumSize()))
{
// Make the texture 2 times bigger
auto newTexture = sf::Texture::create(textureSize * 2u);
if (!newTexture)
Texture newTexture;
if (!newTexture.resize(textureSize * 2u))
{
err() << "Failed to create new page texture" << std::endl;
return {{0, 0}, {2, 2}};
}
newTexture->setSmooth(m_isSmooth);
newTexture->update(page.texture);
page.texture.swap(*newTexture);
newTexture.setSmooth(m_isSmooth);
newTexture.update(page.texture);
page.texture.swap(newTexture);
}
else
{
@ -739,7 +804,7 @@ bool Font::setCurrentSize(unsigned int characterSize) const
////////////////////////////////////////////////////////////
std::optional<Font::Page> Font::Page::create(bool smooth)
Font::Page::Page(bool smooth)
{
// Make sure that the texture is initialized by default
Image image({128, 128}, Color::Transparent);
@ -750,21 +815,12 @@ std::optional<Font::Page> Font::Page::create(bool smooth)
image.setPixel({x, y}, Color::White);
// Create the texture
auto texture = sf::Texture::loadFromImage(image);
if (!texture)
if (!texture.loadFromImage(image))
{
err() << "Failed to load font page texture" << std::endl;
return std::nullopt;
}
texture->setSmooth(smooth);
return Page(std::move(*texture));
}
////////////////////////////////////////////////////////////
Font::Page::Page(Texture&& theTexture) : texture(std::move(theTexture))
{
texture.setSmooth(smooth);
}
} // namespace sf

View File

@ -97,6 +97,20 @@ namespace sf
{
////////////////////////////////////////////////////////////
Image::Image(Vector2u size, Color color)
{
resize(size, color);
}
////////////////////////////////////////////////////////////
Image::Image(Vector2u size, const std::uint8_t* pixels)
{
resize(size, pixels);
}
////////////////////////////////////////////////////////////
void Image::resize(Vector2u size, Color color)
{
if (size.x && size.y)
{
@ -132,7 +146,7 @@ Image::Image(Vector2u size, Color color)
////////////////////////////////////////////////////////////
Image::Image(Vector2u size, const std::uint8_t* pixels)
void Image::resize(Vector2u size, const std::uint8_t* pixels)
{
if (pixels && size.x && size.y)
{
@ -157,13 +171,7 @@ Image::Image(Vector2u size, const std::uint8_t* pixels)
////////////////////////////////////////////////////////////
Image::Image(Vector2u size, std::vector<std::uint8_t>&& pixels) : m_size(size), m_pixels(std::move(pixels))
{
}
////////////////////////////////////////////////////////////
std::optional<Image> Image::loadFromFile(const std::filesystem::path& filename)
bool Image::loadFromFile(const std::filesystem::path& filename)
{
#ifdef SFML_SYSTEM_ANDROID
@ -175,6 +183,9 @@ std::optional<Image> Image::loadFromFile(const std::filesystem::path& filename)
#endif
// Clear the array (just in case)
m_pixels.clear();
// Load the image and get a pointer to the pixels in memory
int width = 0;
int height = 0;
@ -183,24 +194,32 @@ std::optional<Image> Image::loadFromFile(const std::filesystem::path& filename)
if (ptr)
{
return Image(Vector2u(Vector2i(width, height)), {ptr.get(), ptr.get() + width * height * 4});
}
// Assign the image properties
m_size = Vector2u(Vector2i(width, height));
// Copy the loaded pixels to the pixel buffer
m_pixels.assign(ptr.get(), ptr.get() + width * height * 4);
return true;
}
// Error, failed to load the image
err() << "Failed to load image\n"
<< formatDebugPathInfo(filename) << "\nReason: " << stbi_failure_reason() << std::endl;
return std::nullopt;
return false;
}
////////////////////////////////////////////////////////////
std::optional<Image> Image::loadFromMemory(const void* data, std::size_t size)
bool Image::loadFromMemory(const void* data, std::size_t size)
{
// Check input parameters
if (data && size)
{
// Clear the array (just in case)
m_pixels.clear();
// Load the image and get a pointer to the pixels in memory
int width = 0;
int height = 0;
@ -211,28 +230,37 @@ std::optional<Image> Image::loadFromMemory(const void* data, std::size_t size)
if (ptr)
{
return Image(Vector2u(Vector2i(width, height)), {ptr.get(), ptr.get() + width * height * 4});
// Assign the image properties
m_size = Vector2u(Vector2i(width, height));
// Copy the loaded pixels to the pixel buffer
m_pixels.assign(ptr.get(), ptr.get() + width * height * 4);
return true;
}
// Error, failed to load the image
err() << "Failed to load image from memory. Reason: " << stbi_failure_reason() << std::endl;
return std::nullopt;
return false;
}
err() << "Failed to load image from memory, no data provided" << std::endl;
return std::nullopt;
return false;
}
////////////////////////////////////////////////////////////
std::optional<Image> Image::loadFromStream(InputStream& stream)
bool Image::loadFromStream(InputStream& stream)
{
// Clear the array (just in case)
m_pixels.clear();
// Make sure that the stream's reading position is at the beginning
if (!stream.seek(0).has_value())
if (!stream.seek(0))
{
err() << "Failed to seek image stream" << std::endl;
return std::nullopt;
return false;
}
// Setup the stb_image callbacks
@ -249,13 +277,54 @@ std::optional<Image> Image::loadFromStream(InputStream& stream)
if (ptr)
{
return Image(Vector2u(Vector2i(width, height)), {ptr.get(), ptr.get() + width * height * 4});
// Assign the image properties
m_size = Vector2u(Vector2i(width, height));
// Copy the loaded pixels to the pixel buffer
m_pixels.assign(ptr.get(), ptr.get() + width * height * 4);
return true;
}
// Error, failed to load the image
err() << "Failed to load image from stream. Reason: " << stbi_failure_reason() << std::endl;
return false;
}
////////////////////////////////////////////////////////////
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;
}

View File

@ -34,9 +34,15 @@
#include <memory>
#include <ostream>
#include <cassert>
namespace sf
{
////////////////////////////////////////////////////////////
RenderTexture::RenderTexture() = default;
////////////////////////////////////////////////////////////
RenderTexture::~RenderTexture() = default;
@ -50,43 +56,53 @@ RenderTexture& RenderTexture::operator=(RenderTexture&&) noexcept = default;
////////////////////////////////////////////////////////////
std::optional<RenderTexture> RenderTexture::create(Vector2u size, const ContextSettings& settings)
bool RenderTexture::resize(Vector2u size, const ContextSettings& settings)
{
// Create the texture
auto texture = sf::Texture::create(size, settings.sRgbCapable);
if (!texture)
// Set texture to be in sRGB scale if requested
if (!m_texture.resize(size, settings.sRgbCapable))
{
err() << "Impossible to create render texture (failed to create the target texture)" << std::endl;
return std::nullopt;
return false;
}
RenderTexture renderTexture(std::move(*texture));
// We disable smoothing by default for render textures
renderTexture.setSmooth(false);
setSmooth(false);
// Create the implementation
if (priv::RenderTextureImplFBO::isAvailable())
{
// Use frame-buffer object (FBO)
renderTexture.m_impl = std::make_unique<priv::RenderTextureImplFBO>();
m_impl = std::make_unique<priv::RenderTextureImplFBO>();
// Mark the texture as being a framebuffer object attachment
renderTexture.m_texture.m_fboAttachment = true;
m_texture.m_fboAttachment = true;
}
else
{
// Use default implementation
renderTexture.m_impl = std::make_unique<priv::RenderTextureImplDefault>();
m_impl = std::make_unique<priv::RenderTextureImplDefault>();
}
// Initialize the render texture
// We pass the actual size of our texture since OpenGL ES requires that all attachments have identical sizes
if (!renderTexture.m_impl->create(renderTexture.m_texture.m_actualSize, renderTexture.m_texture.m_texture, settings))
return std::nullopt;
if (!m_impl->create(m_texture.m_actualSize, m_texture.m_texture, settings))
return false;
// We can now initialize the render target part
renderTexture.initialize();
RenderTarget::initialize();
return true;
}
////////////////////////////////////////////////////////////
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;
}
@ -143,7 +159,7 @@ bool RenderTexture::generateMipmap()
bool RenderTexture::setActive(bool active)
{
// Update RenderTarget tracking
if (m_impl->activate(active))
if (m_impl && m_impl->activate(active))
return RenderTarget::setActive(active);
return false;
@ -153,6 +169,8 @@ bool RenderTexture::setActive(bool active)
////////////////////////////////////////////////////////////
void RenderTexture::display()
{
if (m_impl)
{
if (priv::RenderTextureImplFBO::isAvailable())
{
// Perform a RenderTarget-only activation if we are using FBOs
@ -170,6 +188,7 @@ void RenderTexture::display()
m_impl->updateTexture(m_texture.m_texture);
m_texture.m_pixelsFlipped = true;
m_texture.invalidateMipmap();
}
}
@ -183,6 +202,7 @@ Vector2u RenderTexture::getSize() const
////////////////////////////////////////////////////////////
bool RenderTexture::isSrgb() const
{
assert(m_impl && "Must call RenderTexture::create first");
return m_impl->isSrgb();
}
@ -193,11 +213,4 @@ const Texture& RenderTexture::getTexture() const
return m_texture;
}
////////////////////////////////////////////////////////////
RenderTexture::RenderTexture(Texture&& texture) : m_texture(std::move(texture))
{
}
} // namespace sf

View File

@ -45,7 +45,6 @@
#include <utility>
#include <vector>
#include <cassert>
#include <cstdint>
#ifndef SFML_OPENGL_ES
@ -252,10 +251,10 @@ Shader& Shader::operator=(Shader&& right) noexcept
return *this;
}
// Explicit scope for RAII
if (m_shaderProgram)
{
// Destroy effect program
const TransientContextLock lock;
assert(m_shaderProgram);
glCheck(GLEXT_glDeleteObject(castToGlHandle(m_shaderProgram)));
}
@ -268,14 +267,14 @@ Shader& Shader::operator=(Shader&& right) noexcept
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& filename, Type type)
bool Shader::loadFromFile(const std::filesystem::path& filename, Type type)
{
// Read the file
std::vector<char> shader;
if (!getFileContents(filename, shader))
{
err() << "Failed to open shader file\n" << formatDebugPathInfo(filename) << std::endl;
return std::nullopt;
return false;
}
// Compile the shader program
@ -290,7 +289,7 @@ std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& filename
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename,
bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename,
const std::filesystem::path& fragmentShaderFilename)
{
// Read the vertex shader file
@ -298,7 +297,7 @@ std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& vertexSh
if (!getFileContents(vertexShaderFilename, vertexShader))
{
err() << "Failed to open vertex shader file\n" << formatDebugPathInfo(vertexShaderFilename) << std::endl;
return std::nullopt;
return false;
}
// Read the fragment shader file
@ -306,7 +305,7 @@ std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& vertexSh
if (!getFileContents(fragmentShaderFilename, fragmentShader))
{
err() << "Failed to open fragment shader file\n" << formatDebugPathInfo(fragmentShaderFilename) << std::endl;
return std::nullopt;
return false;
}
// Compile the shader program
@ -315,7 +314,7 @@ std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& vertexSh
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename,
bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename,
const std::filesystem::path& geometryShaderFilename,
const std::filesystem::path& fragmentShaderFilename)
{
@ -324,7 +323,7 @@ std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& vertexSh
if (!getFileContents(vertexShaderFilename, vertexShader))
{
err() << "Failed to open vertex shader file\n" << formatDebugPathInfo(vertexShaderFilename) << std::endl;
return std::nullopt;
return false;
}
// Read the geometry shader file
@ -332,7 +331,7 @@ std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& vertexSh
if (!getFileContents(geometryShaderFilename, geometryShader))
{
err() << "Failed to open geometry shader file\n" << formatDebugPathInfo(geometryShaderFilename) << std::endl;
return std::nullopt;
return false;
}
// Read the fragment shader file
@ -340,7 +339,7 @@ std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& vertexSh
if (!getFileContents(fragmentShaderFilename, fragmentShader))
{
err() << "Failed to open fragment shader file\n" << formatDebugPathInfo(fragmentShaderFilename) << std::endl;
return std::nullopt;
return false;
}
// Compile the shader program
@ -349,7 +348,7 @@ std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& vertexSh
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromMemory(std::string_view shader, Type type)
bool Shader::loadFromMemory(std::string_view shader, Type type)
{
// Compile the shader program
if (type == Type::Vertex)
@ -363,7 +362,7 @@ std::optional<Shader> Shader::loadFromMemory(std::string_view shader, Type type)
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromMemory(std::string_view vertexShader, std::string_view fragmentShader)
bool Shader::loadFromMemory(std::string_view vertexShader, std::string_view fragmentShader)
{
// Compile the shader program
return compile(vertexShader, {}, fragmentShader);
@ -371,9 +370,7 @@ std::optional<Shader> Shader::loadFromMemory(std::string_view vertexShader, std:
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromMemory(std::string_view vertexShader,
std::string_view geometryShader,
std::string_view fragmentShader)
bool Shader::loadFromMemory(std::string_view vertexShader, std::string_view geometryShader, std::string_view fragmentShader)
{
// Compile the shader program
return compile(vertexShader, geometryShader, fragmentShader);
@ -381,14 +378,14 @@ std::optional<Shader> Shader::loadFromMemory(std::string_view vertexShader,
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromStream(InputStream& stream, Type type)
bool Shader::loadFromStream(InputStream& stream, Type type)
{
// Read the shader code from the stream
std::vector<char> shader;
if (!getStreamContents(stream, shader))
{
err() << "Failed to read shader from stream" << std::endl;
return std::nullopt;
return false;
}
// Compile the shader program
@ -403,14 +400,14 @@ std::optional<Shader> Shader::loadFromStream(InputStream& stream, Type type)
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& fragmentShaderStream)
bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& fragmentShaderStream)
{
// Read the vertex shader code from the stream
std::vector<char> vertexShader;
if (!getStreamContents(vertexShaderStream, vertexShader))
{
err() << "Failed to read vertex shader from stream" << std::endl;
return std::nullopt;
return false;
}
// Read the fragment shader code from the stream
@ -418,7 +415,7 @@ std::optional<Shader> Shader::loadFromStream(InputStream& vertexShaderStream, In
if (!getStreamContents(fragmentShaderStream, fragmentShader))
{
err() << "Failed to read fragment shader from stream" << std::endl;
return std::nullopt;
return false;
}
// Compile the shader program
@ -427,16 +424,14 @@ std::optional<Shader> Shader::loadFromStream(InputStream& vertexShaderStream, In
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromStream(InputStream& vertexShaderStream,
InputStream& geometryShaderStream,
InputStream& fragmentShaderStream)
bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& geometryShaderStream, InputStream& fragmentShaderStream)
{
// Read the vertex shader code from the stream
std::vector<char> vertexShader;
if (!getStreamContents(vertexShaderStream, vertexShader))
{
err() << "Failed to read vertex shader from stream" << std::endl;
return std::nullopt;
return false;
}
// Read the geometry shader code from the stream
@ -444,7 +439,7 @@ std::optional<Shader> Shader::loadFromStream(InputStream& vertexShaderStream,
if (!getStreamContents(geometryShaderStream, geometryShader))
{
err() << "Failed to read geometry shader from stream" << std::endl;
return std::nullopt;
return false;
}
// Read the fragment shader code from the stream
@ -452,7 +447,7 @@ std::optional<Shader> Shader::loadFromStream(InputStream& vertexShaderStream,
if (!getStreamContents(fragmentShaderStream, fragmentShader))
{
err() << "Failed to read fragment shader from stream" << std::endl;
return std::nullopt;
return false;
}
// Compile the shader program
@ -460,6 +455,121 @@ std::optional<Shader> Shader::loadFromStream(InputStream& vertexShaderStream,
}
////////////////////////////////////////////////////////////
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)
{
@ -581,8 +691,8 @@ void Shader::setUniform(const std::string& name, const Glsl::Mat4& matrix)
////////////////////////////////////////////////////////////
void Shader::setUniform(const std::string& name, const Texture& texture)
{
assert(m_shaderProgram);
if (m_shaderProgram)
{
const TransientContextLock lock;
// Find the location of the variable in the shader
@ -609,18 +719,20 @@ void Shader::setUniform(const std::string& name, const Texture& texture)
it->second = &texture;
}
}
}
}
////////////////////////////////////////////////////////////
void Shader::setUniform(const std::string& name, CurrentTextureType)
{
assert(m_shaderProgram);
if (m_shaderProgram)
{
const TransientContextLock lock;
// Find the location of the variable in the shader
m_currentTexture = getUniformLocation(name);
}
}
@ -772,15 +884,7 @@ bool Shader::isGeometryAvailable()
////////////////////////////////////////////////////////////
Shader::Shader(unsigned int shaderProgram) : m_shaderProgram(shaderProgram)
{
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::compile(std::string_view vertexShaderCode,
std::string_view geometryShaderCode,
std::string_view fragmentShaderCode)
bool Shader::compile(std::string_view vertexShaderCode, std::string_view geometryShaderCode, std::string_view fragmentShaderCode)
{
const TransientContextLock lock;
@ -789,15 +893,15 @@ std::optional<Shader> Shader::compile(std::string_view vertexShaderCode,
{
err() << "Failed to create a shader: your system doesn't support shaders "
<< "(you should test Shader::isAvailable() before trying to use the Shader class)" << std::endl;
return std::nullopt;
return false;
}
// Make sure we can use geometry shaders
if (geometryShaderCode.data() && !isGeometryAvailable())
if (!geometryShaderCode.empty() && !isGeometryAvailable())
{
err() << "Failed to create a shader: your system doesn't support geometry shaders "
<< "(you should test Shader::isGeometryAvailable() before trying to use geometry shaders)" << std::endl;
return std::nullopt;
return false;
}
// Create the program
@ -805,7 +909,7 @@ std::optional<Shader> Shader::compile(std::string_view vertexShaderCode,
glCheck(shaderProgram = GLEXT_glCreateProgramObject());
// Create the vertex shader if needed
if (vertexShaderCode.data())
if (!vertexShaderCode.empty())
{
// Create and compile the shader
GLEXT_GLhandle vertexShader{};
@ -825,7 +929,7 @@ std::optional<Shader> Shader::compile(std::string_view vertexShaderCode,
err() << "Failed to compile vertex shader:" << '\n' << log << std::endl;
glCheck(GLEXT_glDeleteObject(vertexShader));
glCheck(GLEXT_glDeleteObject(shaderProgram));
return std::nullopt;
return false;
}
// Attach the shader to the program, and delete it (not needed anymore)
@ -834,7 +938,7 @@ std::optional<Shader> Shader::compile(std::string_view vertexShaderCode,
}
// Create the geometry shader if needed
if (geometryShaderCode.data())
if (!geometryShaderCode.empty())
{
// Create and compile the shader
const GLEXT_GLhandle geometryShader = GLEXT_glCreateShaderObject(GLEXT_GL_GEOMETRY_SHADER);
@ -853,7 +957,7 @@ std::optional<Shader> Shader::compile(std::string_view vertexShaderCode,
err() << "Failed to compile geometry shader:" << '\n' << log << std::endl;
glCheck(GLEXT_glDeleteObject(geometryShader));
glCheck(GLEXT_glDeleteObject(shaderProgram));
return std::nullopt;
return false;
}
// Attach the shader to the program, and delete it (not needed anymore)
@ -862,7 +966,7 @@ std::optional<Shader> Shader::compile(std::string_view vertexShaderCode,
}
// Create the fragment shader if needed
if (fragmentShaderCode.data())
if (!fragmentShaderCode.empty())
{
// Create and compile the shader
GLEXT_GLhandle fragmentShader{};
@ -882,7 +986,7 @@ std::optional<Shader> Shader::compile(std::string_view vertexShaderCode,
err() << "Failed to compile fragment shader:" << '\n' << log << std::endl;
glCheck(GLEXT_glDeleteObject(fragmentShader));
glCheck(GLEXT_glDeleteObject(shaderProgram));
return std::nullopt;
return false;
}
// Attach the shader to the program, and delete it (not needed anymore)
@ -902,14 +1006,28 @@ std::optional<Shader> Shader::compile(std::string_view vertexShaderCode,
glCheck(GLEXT_glGetInfoLog(shaderProgram, sizeof(log), nullptr, log));
err() << "Failed to link shader:" << '\n' << log << std::endl;
glCheck(GLEXT_glDeleteObject(shaderProgram));
return std::nullopt;
return false;
}
// Destroy the shader if it was already created
if (m_shaderProgram)
{
glCheck(GLEXT_glDeleteObject(castToGlHandle(m_shaderProgram)));
m_shaderProgram = 0;
}
// Reset the internal state
m_currentTexture = -1;
m_textures.clear();
m_uniforms.clear();
m_shaderProgram = castFromGlHandle(shaderProgram);
// Force an OpenGL flush, so that the shader will appear updated
// in all contexts immediately (solves problems in multi-threaded apps)
glCheck(glFlush());
return Shader(castFromGlHandle(shaderProgram));
return true;
}
@ -972,14 +1090,84 @@ Shader& Shader::operator=(Shader&& right) noexcept = default;
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& /* filename */, Type /* type */)
bool Shader::loadFromFile(const std::filesystem::path& /* filename */, Type /* type */)
{
return false;
}
////////////////////////////////////////////////////////////
bool Shader::loadFromFile(const std::filesystem::path& /* vertexShaderFilename */,
const std::filesystem::path& /* fragmentShaderFilename */)
{
return false;
}
////////////////////////////////////////////////////////////
bool Shader::loadFromFile(const std::filesystem::path& /* vertexShaderFilename */,
const std::filesystem::path& /* geometryShaderFilename */,
const std::filesystem::path& /* fragmentShaderFilename */)
{
return false;
}
////////////////////////////////////////////////////////////
bool Shader::loadFromMemory(std::string_view /* shader */, Type /* type */)
{
return false;
}
////////////////////////////////////////////////////////////
bool Shader::loadFromMemory(std::string_view /* vertexShader */, std::string_view /* fragmentShader */)
{
return false;
}
////////////////////////////////////////////////////////////
bool Shader::loadFromMemory(std::string_view /* vertexShader */,
std::string_view /* geometryShader */,
std::string_view /* fragmentShader */)
{
return false;
}
////////////////////////////////////////////////////////////
bool Shader::loadFromStream(InputStream& /* stream */, Type /* type */)
{
return false;
}
////////////////////////////////////////////////////////////
bool Shader::loadFromStream(InputStream& /* vertexShaderStream */, InputStream& /* fragmentShaderStream */)
{
return false;
}
////////////////////////////////////////////////////////////
bool Shader::loadFromStream(InputStream& /* vertexShaderStream */,
InputStream& /* geometryShaderStream */,
InputStream& /* fragmentShaderStream */)
{
return false;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::createFromFile(const std::filesystem::path& /* filename */, Type /* type */)
{
return std::nullopt;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& /* vertexShaderFilename */,
std::optional<Shader> Shader::createFromFile(const std::filesystem::path& /* vertexShaderFilename */,
const std::filesystem::path& /* fragmentShaderFilename */)
{
return std::nullopt;
@ -987,7 +1175,7 @@ std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& /* verte
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& /* vertexShaderFilename */,
std::optional<Shader> Shader::createFromFile(const std::filesystem::path& /* vertexShaderFilename */,
const std::filesystem::path& /* geometryShaderFilename */,
const std::filesystem::path& /* fragmentShaderFilename */)
{
@ -996,21 +1184,21 @@ std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& /* verte
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromMemory(std::string_view /* shader */, Type /* type */)
std::optional<Shader> Shader::createFromMemory(std::string_view /* shader */, Type /* type */)
{
return std::nullopt;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromMemory(std::string_view /* vertexShader */, std::string_view /* fragmentShader */)
std::optional<Shader> Shader::createFromMemory(std::string_view /* vertexShader */, std::string_view /* fragmentShader */)
{
return std::nullopt;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromMemory(std::string_view /* vertexShader */,
std::optional<Shader> Shader::createFromMemory(std::string_view /* vertexShader */,
std::string_view /* geometryShader */,
std::string_view /* fragmentShader */)
{
@ -1019,21 +1207,21 @@ std::optional<Shader> Shader::loadFromMemory(std::string_view /* vertexShader */
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromStream(InputStream& /* stream */, Type /* type */)
std::optional<Shader> Shader::createFromStream(InputStream& /* stream */, Type /* type */)
{
return std::nullopt;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromStream(InputStream& /* vertexShaderStream */, InputStream& /* fragmentShaderStream */)
std::optional<Shader> Shader::createFromStream(InputStream& /* vertexShaderStream */, InputStream& /* fragmentShaderStream */)
{
return std::nullopt;
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::loadFromStream(InputStream& /* vertexShaderStream */,
std::optional<Shader> Shader::createFromStream(InputStream& /* vertexShaderStream */,
InputStream& /* geometryShaderStream */,
InputStream& /* fragmentShaderStream */)
{
@ -1201,17 +1389,11 @@ bool Shader::isGeometryAvailable()
////////////////////////////////////////////////////////////
Shader::Shader(unsigned int shaderProgram) : m_shaderProgram(shaderProgram)
{
}
////////////////////////////////////////////////////////////
std::optional<Shader> Shader::compile(std::string_view /* vertexShaderCode */,
bool Shader::compile(std::string_view /* vertexShaderCode */,
std::string_view /* geometryShaderCode */,
std::string_view /* fragmentShaderCode */)
{
return std::nullopt;
return false;
}

View File

@ -66,12 +66,7 @@ std::uint64_t getUniqueId() noexcept
namespace sf
{
////////////////////////////////////////////////////////////
Texture::Texture(Vector2u size, Vector2u actualSize, unsigned int texture, bool sRgb) :
m_size(size),
m_actualSize(actualSize),
m_texture(texture),
m_sRgb(sRgb),
m_cacheId(TextureImpl::getUniqueId())
Texture::Texture() : m_cacheId(TextureImpl::getUniqueId())
{
}
@ -84,14 +79,16 @@ m_sRgb(copy.m_sRgb),
m_isRepeated(copy.m_isRepeated),
m_cacheId(TextureImpl::getUniqueId())
{
if (auto texture = create(copy.getSize(), copy.isSrgb()))
if (copy.m_texture)
{
if (resize(copy.getSize(), copy.isSrgb()))
{
*this = std::move(*texture);
update(copy);
}
else
{
err() << "Failed to copy texture, failed to create new texture" << std::endl;
err() << "Failed to copy texture, failed to resize texture" << std::endl;
}
}
}
@ -165,13 +162,13 @@ Texture& Texture::operator=(Texture&& right) noexcept
////////////////////////////////////////////////////////////
std::optional<Texture> Texture::create(Vector2u size, bool sRgb)
bool Texture::resize(Vector2u size, bool sRgb)
{
// Check if texture parameters are valid before creating it
if ((size.x == 0) || (size.y == 0))
{
err() << "Failed to create texture, invalid size (" << size.x << "x" << size.y << ")" << std::endl;
return std::nullopt;
err() << "Failed to resize texture, invalid size (" << size.x << "x" << size.y << ")" << std::endl;
return false;
}
const TransientContextLock lock;
@ -189,16 +186,22 @@ std::optional<Texture> Texture::create(Vector2u size, bool sRgb)
err() << "Failed to create texture, its internal size is too high "
<< "(" << actualSize.x << "x" << actualSize.y << ", "
<< "maximum is " << maxSize << "x" << maxSize << ")" << std::endl;
return std::nullopt;
return false;
}
// Create the OpenGL texture
GLuint glTexture = 0;
glCheck(glGenTextures(1, &glTexture));
assert(glTexture);
// All the validity checks passed, we can store the new texture settings
Texture texture(size, actualSize, glTexture, sRgb);
m_size = size;
m_actualSize = actualSize;
m_pixelsFlipped = false;
m_fboAttachment = false;
// Create the OpenGL texture if it doesn't exist yet
if (!m_texture)
{
GLuint texture = 0;
glCheck(glGenTextures(1, &texture));
m_texture = texture;
}
// Make sure that the current texture binding will be preserved
const priv::TextureSaver save;
@ -222,7 +225,9 @@ std::optional<Texture> Texture::create(Vector2u size, bool sRgb)
static const bool textureSrgb = GLEXT_texture_sRGB;
if (texture.m_sRgb && !textureSrgb)
m_sRgb = sRgb;
if (m_sRgb && !textureSrgb)
{
static bool warned = false;
@ -238,73 +243,64 @@ std::optional<Texture> Texture::create(Vector2u size, bool sRgb)
warned = true;
}
texture.m_sRgb = false;
m_sRgb = false;
}
#ifndef SFML_OPENGL_ES
const GLint textureWrapParam = textureEdgeClamp ? GLEXT_GL_CLAMP_TO_EDGE : GLEXT_GL_CLAMP;
const GLint textureWrapParam = m_isRepeated ? GL_REPEAT : (textureEdgeClamp ? GLEXT_GL_CLAMP_TO_EDGE : GLEXT_GL_CLAMP);
#else
const GLint textureWrapParam = GLEXT_GL_CLAMP_TO_EDGE;
const GLint textureWrapParam = m_isRepeated ? GL_REPEAT : GLEXT_GL_CLAMP_TO_EDGE;
#endif
// Initialize the texture
glCheck(glBindTexture(GL_TEXTURE_2D, texture.m_texture));
glCheck(glBindTexture(GL_TEXTURE_2D, m_texture));
glCheck(glTexImage2D(GL_TEXTURE_2D,
0,
(texture.m_sRgb ? GLEXT_GL_SRGB8_ALPHA8 : GL_RGBA),
static_cast<GLsizei>(texture.m_actualSize.x),
static_cast<GLsizei>(texture.m_actualSize.y),
(m_sRgb ? GLEXT_GL_SRGB8_ALPHA8 : GL_RGBA),
static_cast<GLsizei>(m_actualSize.x),
static_cast<GLsizei>(m_actualSize.y),
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
nullptr));
glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, textureWrapParam));
glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, textureWrapParam));
glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
texture.m_cacheId = TextureImpl::getUniqueId();
glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST));
glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST));
m_cacheId = TextureImpl::getUniqueId();
texture.m_hasMipmap = false;
m_hasMipmap = false;
return texture;
return true;
}
////////////////////////////////////////////////////////////
std::optional<Texture> Texture::loadFromFile(const std::filesystem::path& filename, bool sRgb, const IntRect& area)
bool Texture::loadFromFile(const std::filesystem::path& filename, bool sRgb, const IntRect& area)
{
if (const auto image = sf::Image::loadFromFile(filename))
return loadFromImage(*image, sRgb, area);
err() << "Failed to load texture from file" << std::endl;
return std::nullopt;
Image image;
return image.loadFromFile(filename) && loadFromImage(image, sRgb, area);
}
////////////////////////////////////////////////////////////
std::optional<Texture> Texture::loadFromMemory(const void* data, std::size_t size, bool sRgb, const IntRect& area)
bool Texture::loadFromMemory(const void* data, std::size_t size, bool sRgb, const IntRect& area)
{
if (const auto image = sf::Image::loadFromMemory(data, size))
return loadFromImage(*image, sRgb, area);
err() << "Failed to load texture from memory" << std::endl;
return std::nullopt;
Image image;
return image.loadFromMemory(data, size) && loadFromImage(image, sRgb, area);
}
////////////////////////////////////////////////////////////
std::optional<Texture> Texture::loadFromStream(InputStream& stream, bool sRgb, const IntRect& area)
bool Texture::loadFromStream(InputStream& stream, bool sRgb, const IntRect& area)
{
if (const auto image = sf::Image::loadFromStream(stream))
return loadFromImage(*image, sRgb, area);
err() << "Failed to load texture from stream" << std::endl;
return std::nullopt;
Image image;
return image.loadFromStream(stream) && loadFromImage(image, sRgb, area);
}
////////////////////////////////////////////////////////////
std::optional<Texture> Texture::loadFromImage(const Image& image, bool sRgb, const IntRect& area)
bool Texture::loadFromImage(const Image& image, bool sRgb, const IntRect& area)
{
// Retrieve the image size
const auto size = Vector2i(image.getSize());
@ -314,14 +310,14 @@ std::optional<Texture> Texture::loadFromImage(const Image& image, bool sRgb, con
((area.position.x <= 0) && (area.position.y <= 0) && (area.size.x >= size.x) && (area.size.y >= size.y)))
{
// Load the entire image
if (auto texture = sf::Texture::create(image.getSize(), sRgb))
if (resize(image.getSize(), sRgb))
{
texture->update(image);
return texture;
update(image);
return true;
}
// Error message generated in called function.
return std::nullopt;
return false;
}
// Load a sub-area of the image
@ -334,7 +330,7 @@ std::optional<Texture> Texture::loadFromImage(const Image& image, bool sRgb, con
rectangle.size.y = std::min(rectangle.size.y, size.y - rectangle.position.y);
// Create the texture and upload the pixels
if (auto texture = sf::Texture::create(Vector2u(rectangle.size), sRgb))
if (resize(Vector2u(rectangle.size), sRgb))
{
const TransientContextLock lock;
@ -343,25 +339,85 @@ std::optional<Texture> Texture::loadFromImage(const Image& image, bool sRgb, con
// Copy the pixels to the texture, row by row
const std::uint8_t* pixels = image.getPixelsPtr() + 4 * (rectangle.position.x + (size.x * rectangle.position.y));
glCheck(glBindTexture(GL_TEXTURE_2D, texture->m_texture));
glCheck(glBindTexture(GL_TEXTURE_2D, m_texture));
for (int i = 0; i < rectangle.size.y; ++i)
{
glCheck(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, i, rectangle.size.x, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
pixels += 4 * size.x;
}
glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
texture->m_hasMipmap = false;
glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST));
m_hasMipmap = false;
// Force an OpenGL flush, so that the texture will appear updated
// in all contexts immediately (solves problems in multi-threaded apps)
glCheck(glFlush());
return texture;
return true;
}
// Error message generated in called function.
return false;
}
////////////////////////////////////////////////////////////
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;
}
@ -376,7 +432,8 @@ Vector2u Texture::getSize() const
Image Texture::copyToImage() const
{
// Easy case: empty texture
assert(m_texture && "Texture::copyToImage Cannot copy empty texture to image");
if (!m_texture)
return {};
const TransientContextLock lock;

View File

@ -32,7 +32,6 @@
#endif
#include <memory>
#include <cassert>
#include <cstddef>
namespace sf
@ -44,6 +43,10 @@ void FileInputStream::FileCloser::operator()(std::FILE* file)
}
////////////////////////////////////////////////////////////
FileInputStream::FileInputStream() = default;
////////////////////////////////////////////////////////////
FileInputStream::~FileInputStream() = default;
@ -57,24 +60,33 @@ FileInputStream& FileInputStream::operator=(FileInputStream&&) noexcept = defaul
////////////////////////////////////////////////////////////
std::optional<FileInputStream> FileInputStream::open(const std::filesystem::path& filename)
bool FileInputStream::open(const std::filesystem::path& filename)
{
#ifdef SFML_SYSTEM_ANDROID
if (priv::getActivityStatesPtr() != nullptr)
{
auto androidFile = std::make_unique<priv::ResourceStream>(filename);
if (androidFile->tell().has_value())
return FileInputStream(std::move(androidFile));
return std::nullopt;
m_androidFile = std::make_unique<priv::ResourceStream>(filename);
return m_androidFile->tell().has_value();
}
#endif
#ifdef SFML_SYSTEM_WINDOWS
if (auto file = std::unique_ptr<std::FILE, FileCloser>(_wfopen(filename.c_str(), L"rb")))
m_file.reset(_wfopen(filename.c_str(), L"rb"));
#else
if (auto file = std::unique_ptr<std::FILE, FileCloser>(std::fopen(filename.c_str(), "rb")))
m_file.reset(std::fopen(filename.c_str(), "rb"));
#endif
return FileInputStream(std::move(file));
return m_file != nullptr;
}
////////////////////////////////////////////////////////////
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;
}
@ -84,11 +96,13 @@ std::optional<std::size_t> FileInputStream::read(void* data, std::size_t size)
#ifdef SFML_SYSTEM_ANDROID
if (priv::getActivityStatesPtr() != nullptr)
{
assert(m_androidFile);
if (!m_androidFile)
return std::nullopt;
return m_androidFile->read(data, size);
}
#endif
assert(m_file);
if (!m_file)
return std::nullopt;
return std::fread(data, 1, size, m_file.get());
}
@ -99,11 +113,13 @@ std::optional<std::size_t> FileInputStream::seek(std::size_t position)
#ifdef SFML_SYSTEM_ANDROID
if (priv::getActivityStatesPtr() != nullptr)
{
assert(m_androidFile);
if (!m_androidFile)
return std::nullopt;
return m_androidFile->seek(position);
}
#endif
assert(m_file);
if (!m_file)
return std::nullopt;
if (std::fseek(m_file.get(), static_cast<long>(position), SEEK_SET))
return std::nullopt;
@ -117,11 +133,13 @@ std::optional<std::size_t> FileInputStream::tell()
#ifdef SFML_SYSTEM_ANDROID
if (priv::getActivityStatesPtr() != nullptr)
{
assert(m_androidFile);
if (!m_androidFile)
return std::nullopt;
return m_androidFile->tell();
}
#endif
assert(m_file);
if (!m_file)
return std::nullopt;
const auto position = std::ftell(m_file.get());
return position < 0 ? std::nullopt : std::optional<std::size_t>(position);
}
@ -133,11 +151,13 @@ std::optional<std::size_t> FileInputStream::getSize()
#ifdef SFML_SYSTEM_ANDROID
if (priv::getActivityStatesPtr() != nullptr)
{
assert(m_androidFile);
if (!m_androidFile)
return std::nullopt;
return m_androidFile->getSize();
}
#endif
assert(m_file);
if (!m_file)
return std::nullopt;
const auto position = tell().value();
std::fseek(m_file.get(), 0, SEEK_END);
const std::optional size = tell();
@ -148,19 +168,4 @@ std::optional<std::size_t> FileInputStream::getSize()
return size;
}
////////////////////////////////////////////////////////////
FileInputStream::FileInputStream(std::unique_ptr<std::FILE, FileCloser>&& file) : m_file(std::move(file))
{
}
////////////////////////////////////////////////////////////
#ifdef SFML_SYSTEM_ANDROID
FileInputStream::FileInputStream(std::unique_ptr<priv::ResourceStream>&& androidFile) :
m_androidFile(std::move(androidFile))
{
}
#endif
} // namespace sf

View File

@ -29,24 +29,33 @@
#include <algorithm>
#include <cassert>
#include <cstring>
namespace sf
{
////////////////////////////////////////////////////////////
MemoryInputStream::MemoryInputStream(const void* data, std::size_t sizeInBytes) :
m_data(static_cast<const std::byte*>(data)),
m_size(sizeInBytes)
MemoryInputStream::MemoryInputStream(const void* data, std::size_t sizeInBytes)
{
assert(m_data && "MemoryInputStream must be initialized with non-null data");
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;
}
////////////////////////////////////////////////////////////
std::optional<std::size_t> MemoryInputStream::read(void* data, std::size_t size)
{
if (!m_data)
return std::nullopt;
const std::size_t count = std::min(size, m_size - m_offset);
if (count > 0)
{
@ -61,6 +70,9 @@ std::optional<std::size_t> MemoryInputStream::read(void* data, std::size_t size)
////////////////////////////////////////////////////////////
std::optional<std::size_t> MemoryInputStream::seek(std::size_t position)
{
if (!m_data)
return std::nullopt;
m_offset = position < m_size ? position : m_size;
return m_offset;
}
@ -69,6 +81,9 @@ std::optional<std::size_t> MemoryInputStream::seek(std::size_t position)
////////////////////////////////////////////////////////////
std::optional<std::size_t> MemoryInputStream::tell()
{
if (!m_data)
return std::nullopt;
return m_offset;
}
@ -76,6 +91,9 @@ std::optional<std::size_t> MemoryInputStream::tell()
////////////////////////////////////////////////////////////
std::optional<std::size_t> MemoryInputStream::getSize()
{
if (!m_data)
return std::nullopt;
return m_size;
}

View File

@ -56,11 +56,11 @@ Cursor& Cursor::operator=(Cursor&&) noexcept = default;
////////////////////////////////////////////////////////////
std::optional<Cursor> Cursor::loadFromPixels(const std::uint8_t* pixels, Vector2u size, Vector2u hotspot)
std::optional<Cursor> Cursor::createFromPixels(const std::uint8_t* pixels, Vector2u size, Vector2u hotspot)
{
if ((pixels == nullptr) || (size.x == 0) || (size.y == 0))
{
err() << "Failed to load cursor from pixels (invalid arguments)" << std::endl;
err() << "Failed to create cursor from pixels (invalid arguments)" << std::endl;
return std::nullopt;
}
@ -76,7 +76,7 @@ std::optional<Cursor> Cursor::loadFromPixels(const std::uint8_t* pixels, Vector2
////////////////////////////////////////////////////////////
std::optional<Cursor> Cursor::loadFromSystem(Type type)
std::optional<Cursor> Cursor::createFromSystem(Type type)
{
Cursor cursor;
if (!cursor.m_impl->loadFromSystem(type))

View File

@ -74,7 +74,7 @@ public:
////////////////////////////////////////////////////////////
/// \brief Create a cursor with the provided image
///
/// Refer to sf::Cursor::loadFromPixels().
/// Refer to sf::Cursor::createFromPixels().
///
////////////////////////////////////////////////////////////
bool loadFromPixels(const std::uint8_t* pixels, Vector2u size, Vector2u hotspot);
@ -82,7 +82,7 @@ public:
////////////////////////////////////////////////////////////
/// \brief Create a native system cursor
///
/// Refer to sf::Cursor::loadFromSystem().
/// Refer to sf::Cursor::createFromSystem().
///
////////////////////////////////////////////////////////////
bool loadFromSystem(Cursor::Type type);

View File

@ -15,25 +15,37 @@ TEST_CASE("[Audio] sf::InputSoundFile")
{
SECTION("Type traits")
{
STATIC_CHECK(!std::is_default_constructible_v<sf::InputSoundFile>);
STATIC_CHECK(!std::is_copy_constructible_v<sf::InputSoundFile>);
STATIC_CHECK(!std::is_copy_assignable_v<sf::InputSoundFile>);
STATIC_CHECK(std::is_nothrow_move_constructible_v<sf::InputSoundFile>);
STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::InputSoundFile>);
}
SECTION("Construction")
{
const sf::InputSoundFile inputSoundFile;
CHECK(inputSoundFile.getSampleCount() == 0);
CHECK(inputSoundFile.getChannelCount() == 0);
CHECK(inputSoundFile.getSampleRate() == 0);
CHECK(inputSoundFile.getDuration() == sf::Time::Zero);
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
SECTION("openFromFile()")
{
sf::InputSoundFile inputSoundFile;
SECTION("Invalid file")
{
CHECK(!sf::InputSoundFile::openFromFile("does/not/exist.wav"));
CHECK(!inputSoundFile.openFromFile("does/not/exist.wav"));
}
SECTION("Valid file")
{
SECTION("flac")
{
const auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.flac").value();
REQUIRE(inputSoundFile.openFromFile("Audio/ding.flac"));
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 44'100);
@ -44,7 +56,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("mp3")
{
const auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.mp3").value();
REQUIRE(inputSoundFile.openFromFile("Audio/ding.mp3"));
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 44'100);
@ -55,7 +67,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("ogg")
{
const auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/doodle_pop.ogg").value();
REQUIRE(inputSoundFile.openFromFile("Audio/doodle_pop.ogg"));
CHECK(inputSoundFile.getSampleCount() == 2'116'992);
CHECK(inputSoundFile.getChannelCount() == 2);
CHECK(inputSoundFile.getSampleRate() == 44'100);
@ -66,7 +78,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("wav")
{
const auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/killdeer.wav").value();
REQUIRE(inputSoundFile.openFromFile("Audio/killdeer.wav"));
CHECK(inputSoundFile.getSampleCount() == 112'941);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 22'050);
@ -80,7 +92,8 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("openFromMemory()")
{
const auto memory = loadIntoMemory("Audio/killdeer.wav");
const auto inputSoundFile = sf::InputSoundFile::openFromMemory(memory.data(), memory.size()).value();
sf::InputSoundFile inputSoundFile;
REQUIRE(inputSoundFile.openFromMemory(memory.data(), memory.size()));
CHECK(inputSoundFile.getSampleCount() == 112'941);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 22'050);
@ -90,11 +103,21 @@ TEST_CASE("[Audio] sf::InputSoundFile")
}
SECTION("openFromStream()")
{
sf::InputSoundFile inputSoundFile;
sf::FileInputStream stream;
SECTION("Invalid stream")
{
CHECK(!inputSoundFile.openFromStream(stream));
}
SECTION("Valid stream")
{
SECTION("flac")
{
auto stream = sf::FileInputStream::open("Audio/ding.flac").value();
const auto inputSoundFile = sf::InputSoundFile::openFromStream(stream).value();
REQUIRE(stream.open("Audio/ding.flac"));
REQUIRE(inputSoundFile.openFromStream(stream));
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 44'100);
@ -105,8 +128,8 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("mp3")
{
auto stream = sf::FileInputStream::open("Audio/ding.mp3").value();
const auto inputSoundFile = sf::InputSoundFile::openFromStream(stream).value();
REQUIRE(stream.open("Audio/ding.mp3"));
REQUIRE(inputSoundFile.openFromStream(stream));
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 44'100);
@ -117,8 +140,8 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("ogg")
{
auto stream = sf::FileInputStream::open("Audio/doodle_pop.ogg").value();
const auto inputSoundFile = sf::InputSoundFile::openFromStream(stream).value();
REQUIRE(stream.open("Audio/doodle_pop.ogg"));
REQUIRE(inputSoundFile.openFromStream(stream));
CHECK(inputSoundFile.getSampleCount() == 2'116'992);
CHECK(inputSoundFile.getChannelCount() == 2);
CHECK(inputSoundFile.getSampleRate() == 44'100);
@ -129,8 +152,127 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("wav")
{
auto stream = sf::FileInputStream::open("Audio/killdeer.wav").value();
const auto inputSoundFile = sf::InputSoundFile::openFromStream(stream).value();
REQUIRE(stream.open("Audio/killdeer.wav"));
REQUIRE(inputSoundFile.openFromStream(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("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);
@ -144,7 +286,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
{
SECTION("flac")
{
auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.flac").value();
auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/ding.flac").value();
inputSoundFile.seek(1'000);
CHECK(inputSoundFile.getTimeOffset() == sf::microseconds(22'675));
CHECK(inputSoundFile.getSampleOffset() == 1'000);
@ -152,7 +294,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("mp3")
{
auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.mp3").value();
auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/ding.mp3").value();
inputSoundFile.seek(1'000);
CHECK(inputSoundFile.getTimeOffset() == sf::microseconds(22'675));
CHECK(inputSoundFile.getSampleOffset() == 1'000);
@ -160,7 +302,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("ogg")
{
auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/doodle_pop.ogg").value();
auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/doodle_pop.ogg").value();
inputSoundFile.seek(1'000);
CHECK(inputSoundFile.getTimeOffset() == sf::microseconds(11'337));
CHECK(inputSoundFile.getSampleOffset() == 1'000);
@ -168,7 +310,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("wav")
{
auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/killdeer.wav").value();
auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/killdeer.wav").value();
inputSoundFile.seek(1'000);
CHECK(inputSoundFile.getTimeOffset() == sf::microseconds(45'351));
CHECK(inputSoundFile.getSampleOffset() == 1'000);
@ -177,7 +319,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("seek(Time)")
{
auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.flac").value();
auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/ding.flac").value();
inputSoundFile.seek(sf::milliseconds(100));
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
@ -189,7 +331,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("read()")
{
auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.flac").value();
auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/ding.flac").value();
SECTION("Null address")
{
@ -207,7 +349,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
{
SECTION("flac")
{
inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.flac").value();
inputSoundFile = sf::InputSoundFile::createFromFile("Audio/ding.flac").value();
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
CHECK(samples == std::array<std::int16_t, 4>{0, 1, -1, 4});
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
@ -216,7 +358,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("mp3")
{
inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.mp3").value();
inputSoundFile = sf::InputSoundFile::createFromFile("Audio/ding.mp3").value();
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
CHECK(samples == std::array<std::int16_t, 4>{0, -2, 0, 2});
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
@ -225,7 +367,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("ogg")
{
inputSoundFile = sf::InputSoundFile::openFromFile("Audio/doodle_pop.ogg").value();
inputSoundFile = sf::InputSoundFile::createFromFile("Audio/doodle_pop.ogg").value();
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
CHECK(samples == std::array<std::int16_t, 4>{-827, -985, -1168, -1319});
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
@ -241,7 +383,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("close()")
{
auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.flac").value();
auto inputSoundFile = sf::InputSoundFile::createFromFile("Audio/ding.flac").value();
inputSoundFile.close();
CHECK(inputSoundFile.getSampleCount() == 0);
CHECK(inputSoundFile.getChannelCount() == 0);

View File

@ -32,16 +32,41 @@ TEST_CASE("[Audio] sf::Music", runAudioDeviceTests())
CHECK(timeSpan.length == sf::Time::Zero);
}
SECTION("Construction")
{
const sf::Music music;
CHECK(music.getDuration() == sf::Time::Zero);
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::Time::Zero);
CHECK(length == sf::Time::Zero);
CHECK(music.getChannelCount() == 0);
CHECK(music.getSampleRate() == 0);
CHECK(music.getStatus() == sf::Music::Status::Stopped);
CHECK(music.getPlayingOffset() == sf::Time::Zero);
CHECK(!music.getLoop());
}
SECTION("openFromFile()")
{
sf::Music music;
SECTION("Invalid file")
{
CHECK(!sf::Music::openFromFile("does/not/exist.wav"));
REQUIRE(!music.openFromFile("does/not/exist.wav"));
CHECK(music.getDuration() == sf::Time::Zero);
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::Time::Zero);
CHECK(length == sf::Time::Zero);
CHECK(music.getChannelCount() == 0);
CHECK(music.getSampleRate() == 0);
CHECK(music.getStatus() == sf::Music::Status::Stopped);
CHECK(music.getPlayingOffset() == sf::Time::Zero);
CHECK(!music.getLoop());
}
SECTION("Valid file")
{
const auto music = sf::Music::openFromFile("Audio/ding.mp3").value();
REQUIRE(music.openFromFile("Audio/ding.mp3"));
CHECK(music.getDuration() == sf::microseconds(1990884));
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::Time::Zero);
@ -56,18 +81,27 @@ TEST_CASE("[Audio] sf::Music", runAudioDeviceTests())
SECTION("openFromMemory()")
{
std::vector<std::byte> memory(10, std::byte{0xCA});
std::vector<std::byte> memory;
sf::Music music;
SECTION("Invalid buffer")
{
CHECK(!sf::Music::openFromMemory(memory.data(), memory.size()));
REQUIRE(!music.openFromMemory(memory.data(), memory.size()));
CHECK(music.getDuration() == sf::Time::Zero);
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::Time::Zero);
CHECK(length == sf::Time::Zero);
CHECK(music.getChannelCount() == 0);
CHECK(music.getSampleRate() == 0);
CHECK(music.getStatus() == sf::Music::Status::Stopped);
CHECK(music.getPlayingOffset() == sf::Time::Zero);
CHECK(!music.getLoop());
}
SECTION("Valid buffer")
{
memory = loadIntoMemory("Audio/ding.flac");
const auto music = sf::Music::openFromMemory(memory.data(), memory.size()).value();
REQUIRE(music.openFromMemory(memory.data(), memory.size()));
CHECK(music.getDuration() == sf::microseconds(1990884));
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::Time::Zero);
@ -82,8 +116,91 @@ TEST_CASE("[Audio] sf::Music", runAudioDeviceTests())
SECTION("openFromStream()")
{
auto stream = sf::FileInputStream::open("Audio/doodle_pop.ogg").value();
const auto music = sf::Music::openFromStream(stream).value();
sf::FileInputStream stream;
sf::Music music;
SECTION("Invalid stream")
{
CHECK(!music.openFromStream(stream));
CHECK(music.getDuration() == sf::Time::Zero);
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::Time::Zero);
CHECK(length == sf::Time::Zero);
CHECK(music.getChannelCount() == 0);
CHECK(music.getSampleRate() == 0);
CHECK(music.getStatus() == sf::Music::Status::Stopped);
CHECK(music.getPlayingOffset() == sf::Time::Zero);
CHECK(!music.getLoop());
}
SECTION("Valid stream")
{
REQUIRE(stream.open("Audio/doodle_pop.ogg"));
REQUIRE(music.openFromStream(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("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);
@ -97,7 +214,7 @@ TEST_CASE("[Audio] sf::Music", runAudioDeviceTests())
SECTION("play/pause/stop")
{
auto music = sf::Music::openFromFile("Audio/ding.mp3").value();
auto music = sf::Music::createFromFile("Audio/ding.mp3").value();
// Wait for background thread to start
music.play();
@ -120,7 +237,7 @@ TEST_CASE("[Audio] sf::Music", runAudioDeviceTests())
SECTION("setLoopPoints()")
{
auto music = sf::Music::openFromFile("Audio/killdeer.wav").value();
auto music = sf::Music::createFromFile("Audio/killdeer.wav").value();
music.setLoopPoints({sf::seconds(1), sf::seconds(2)});
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::seconds(1));

View File

@ -2,7 +2,6 @@
#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_assignable_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>);
}
const auto soundBuffer = sf::SoundBuffer::loadFromFile("Audio/ding.flac").value();
const auto soundBuffer = sf::SoundBuffer::createFromFile("Audio/ding.flac").value();
SECTION("Construction")
{
@ -52,7 +52,7 @@ TEST_CASE("[Audio] sf::Sound", runAudioDeviceTests())
SECTION("Assignment")
{
const sf::SoundBuffer otherSoundBuffer = sf::SoundBuffer::loadFromFile("Audio/ding.flac").value();
const sf::SoundBuffer otherSoundBuffer = sf::SoundBuffer::createFromFile("Audio/ding.flac").value();
sf::Sound soundCopy(otherSoundBuffer);
soundCopy = sound;
CHECK(&soundCopy.getBuffer() == &soundBuffer);
@ -64,7 +64,7 @@ TEST_CASE("[Audio] sf::Sound", runAudioDeviceTests())
SECTION("Set/get buffer")
{
const sf::SoundBuffer otherSoundBuffer = sf::SoundBuffer::loadFromFile("Audio/ding.flac").value();
const sf::SoundBuffer otherSoundBuffer = sf::SoundBuffer::createFromFile("Audio/ding.flac").value();
sf::Sound sound(soundBuffer);
sound.setBuffer(otherSoundBuffer);
CHECK(&sound.getBuffer() == &otherSoundBuffer);

View File

@ -14,7 +14,6 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
{
SECTION("Type traits")
{
STATIC_CHECK(!std::is_default_constructible_v<sf::SoundBuffer>);
STATIC_CHECK(std::is_copy_constructible_v<sf::SoundBuffer>);
STATIC_CHECK(std::is_copy_assignable_v<sf::SoundBuffer>);
STATIC_CHECK(std::is_move_constructible_v<sf::SoundBuffer>);
@ -23,9 +22,19 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
STATIC_CHECK(!std::is_nothrow_move_assignable_v<sf::SoundBuffer>);
}
SECTION("Construction")
{
const sf::SoundBuffer soundBuffer;
CHECK(soundBuffer.getSamples() == nullptr);
CHECK(soundBuffer.getSampleCount() == 0);
CHECK(soundBuffer.getSampleRate() == 44100);
CHECK(soundBuffer.getChannelCount() == 1);
CHECK(soundBuffer.getDuration() == sf::Time::Zero);
}
SECTION("Copy semantics")
{
const auto soundBuffer = sf::SoundBuffer::loadFromFile("Audio/ding.flac").value();
const auto soundBuffer = sf::SoundBuffer::createFromFile("Audio/ding.flac").value();
SECTION("Construction")
{
@ -39,7 +48,7 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
SECTION("Assignment")
{
sf::SoundBuffer soundBufferCopy = sf::SoundBuffer::loadFromFile("Audio/doodle_pop.ogg").value();
sf::SoundBuffer soundBufferCopy = sf::SoundBuffer::createFromFile("Audio/doodle_pop.ogg").value();
soundBufferCopy = soundBuffer;
CHECK(soundBufferCopy.getSamples() != nullptr);
CHECK(soundBufferCopy.getSampleCount() == 87798);
@ -51,14 +60,16 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
SECTION("loadFromFile()")
{
sf::SoundBuffer soundBuffer;
SECTION("Invalid filename")
{
CHECK(!sf::SoundBuffer::loadFromFile("does/not/exist.wav"));
CHECK(!soundBuffer.loadFromFile("does/not/exist.wav"));
}
SECTION("Valid file")
{
const auto soundBuffer = sf::SoundBuffer::loadFromFile("Audio/ding.flac").value();
REQUIRE(soundBuffer.loadFromFile("Audio/ding.flac"));
CHECK(soundBuffer.getSamples() != nullptr);
CHECK(soundBuffer.getSampleCount() == 87798);
CHECK(soundBuffer.getSampleRate() == 44100);
@ -69,16 +80,19 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
SECTION("loadFromMemory()")
{
sf::SoundBuffer soundBuffer;
SECTION("Invalid memory")
{
CHECK(!soundBuffer.loadFromMemory(nullptr, 0));
constexpr std::array<std::byte, 5> memory{};
CHECK(!sf::SoundBuffer::loadFromMemory(memory.data(), memory.size()));
CHECK(!soundBuffer.loadFromMemory(memory.data(), memory.size()));
}
SECTION("Valid memory")
{
const auto memory = loadIntoMemory("Audio/ding.flac");
const auto soundBuffer = sf::SoundBuffer::loadFromMemory(memory.data(), memory.size()).value();
REQUIRE(soundBuffer.loadFromMemory(memory.data(), memory.size()));
CHECK(soundBuffer.getSamples() != nullptr);
CHECK(soundBuffer.getSampleCount() == 87798);
CHECK(soundBuffer.getSampleRate() == 44100);
@ -89,8 +103,68 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
SECTION("loadFromStream()")
{
auto stream = sf::FileInputStream::open("Audio/ding.flac").value();
const auto soundBuffer = sf::SoundBuffer::loadFromStream(stream).value();
sf::FileInputStream stream;
sf::SoundBuffer soundBuffer;
SECTION("Invalid stream")
{
CHECK(!soundBuffer.loadFromStream(stream));
}
SECTION("Valid stream")
{
REQUIRE(stream.open("Audio/ding.flac"));
REQUIRE(soundBuffer.loadFromStream(stream));
CHECK(soundBuffer.getSamples() != nullptr);
CHECK(soundBuffer.getSampleCount() == 87798);
CHECK(soundBuffer.getSampleRate() == 44100);
CHECK(soundBuffer.getChannelCount() == 1);
CHECK(soundBuffer.getDuration() == sf::microseconds(1990884));
}
}
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);
@ -103,11 +177,11 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
const auto filename = std::filesystem::temp_directory_path() / "ding.flac";
{
const auto soundBuffer = sf::SoundBuffer::loadFromFile("Audio/ding.flac").value();
const auto soundBuffer = sf::SoundBuffer::createFromFile("Audio/ding.flac").value();
REQUIRE(soundBuffer.saveToFile(filename));
}
const auto soundBuffer = sf::SoundBuffer::loadFromFile(filename).value();
const auto soundBuffer = sf::SoundBuffer::createFromFile(filename).value();
CHECK(soundBuffer.getSamples() != nullptr);
CHECK(soundBuffer.getSampleCount() == 87798);
CHECK(soundBuffer.getSampleRate() == 44100);

View File

@ -114,22 +114,22 @@ TEST_CASE("[Audio] sf::SoundFileFactory")
SECTION("flac")
{
stream = sf::FileInputStream::open("Audio/ding.flac");
stream = sf::FileInputStream::create("Audio/ding.flac");
}
SECTION("mp3")
{
stream = sf::FileInputStream::open("Audio/ding.mp3");
stream = sf::FileInputStream::create("Audio/ding.mp3");
}
SECTION("ogg")
{
stream = sf::FileInputStream::open("Audio/doodle_pop.ogg");
stream = sf::FileInputStream::create("Audio/doodle_pop.ogg");
}
SECTION("wav")
{
stream = sf::FileInputStream::open("Audio/killdeer.wav");
stream = sf::FileInputStream::create("Audio/killdeer.wav");
}
REQUIRE(stream);

View File

@ -15,23 +15,35 @@ TEST_CASE("[Graphics] sf::Font", runDisplayTests())
{
SECTION("Type traits")
{
STATIC_CHECK(!std::is_default_constructible_v<sf::Font>);
STATIC_CHECK(std::is_copy_constructible_v<sf::Font>);
STATIC_CHECK(std::is_copy_assignable_v<sf::Font>);
STATIC_CHECK(std::is_move_constructible_v<sf::Font>);
STATIC_CHECK(std::is_move_assignable_v<sf::Font>);
}
SECTION("openFromFile()")
SECTION("Construction")
{
SECTION("Invalid filename")
{
CHECK(!sf::Font::openFromFile("does/not/exist.ttf"));
const sf::Font font;
CHECK(font.getInfo().family.empty());
CHECK(!font.hasGlyph(0));
CHECK(font.getLineSpacing(0) == 0);
CHECK(font.getUnderlinePosition(0) == 0);
CHECK(font.getUnderlineThickness(0) == 0);
CHECK(font.isSmooth());
}
SECTION("Valid file")
SECTION("openFromFile()")
{
const auto font = sf::Font::openFromFile("Graphics/tuffy.ttf").value();
sf::Font font;
SECTION("Invalid filename")
{
CHECK(!font.openFromFile("does/not/exist.ttf"));
}
SECTION("Successful load")
{
REQUIRE(font.openFromFile("Graphics/tuffy.ttf"));
CHECK(font.getInfo().family == "Tuffy");
const auto& glyph = font.getGlyph(0x45, 16, false);
CHECK(glyph.advance == 9);
@ -58,17 +70,19 @@ TEST_CASE("[Graphics] sf::Font", runDisplayTests())
SECTION("openFromMemory()")
{
sf::Font font;
SECTION("Invalid data and size")
{
CHECK(!sf::Font::openFromMemory(nullptr, 1));
CHECK(!font.openFromMemory(nullptr, 1));
const std::byte testByte{0xCD};
CHECK(!sf::Font::openFromMemory(&testByte, 0));
CHECK(!font.openFromMemory(&testByte, 0));
}
SECTION("Valid data")
SECTION("Successful load")
{
const auto memory = loadIntoMemory("Graphics/tuffy.ttf");
const auto font = sf::Font::openFromMemory(memory.data(), memory.size()).value();
REQUIRE(font.openFromMemory(memory.data(), memory.size()));
CHECK(font.getInfo().family == "Tuffy");
const auto& glyph = font.getGlyph(0x45, 16, false);
CHECK(glyph.advance == 9);
@ -95,8 +109,117 @@ TEST_CASE("[Graphics] sf::Font", runDisplayTests())
SECTION("openFromStream()")
{
auto stream = sf::FileInputStream::open("Graphics/tuffy.ttf").value();
const auto font = sf::Font::openFromStream(stream).value();
sf::Font font;
sf::FileInputStream stream;
SECTION("Invalid stream")
{
CHECK(!font.openFromStream(stream));
}
SECTION("Successful load")
{
REQUIRE(stream.open("Graphics/tuffy.ttf"));
REQUIRE(font.openFromStream(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("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);
@ -122,7 +245,7 @@ TEST_CASE("[Graphics] sf::Font", runDisplayTests())
SECTION("Set/get smooth")
{
auto font = sf::Font::openFromFile("Graphics/tuffy.ttf").value();
auto font = sf::Font::createFromFile("Graphics/tuffy.ttf").value();
font.setSmooth(false);
CHECK(!font.isSmooth());
}

View File

@ -13,13 +13,19 @@ TEST_CASE("[Graphics] sf::Image")
{
SECTION("Type traits")
{
STATIC_CHECK(!std::is_default_constructible_v<sf::Image>);
STATIC_CHECK(std::is_copy_constructible_v<sf::Image>);
STATIC_CHECK(std::is_copy_assignable_v<sf::Image>);
STATIC_CHECK(std::is_nothrow_move_constructible_v<sf::Image>);
STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::Image>);
}
SECTION("Default constructor")
{
const sf::Image image;
CHECK(image.getSize() == sf::Vector2u());
CHECK(image.getPixelsPtr() == nullptr);
}
SECTION("Construction")
{
SECTION("Vector2 constructor")
@ -78,74 +84,137 @@ TEST_CASE("[Graphics] sf::Image")
}
}
SECTION("Resize")
{
SECTION("resize(Vector2)")
{
sf::Image image;
image.resize(sf::Vector2u(10, 10));
CHECK(image.getSize() == sf::Vector2u(10, 10));
CHECK(image.getPixelsPtr() != nullptr);
for (std::uint32_t i = 0; i < 10; ++i)
{
for (std::uint32_t j = 0; j < 10; ++j)
{
CHECK(image.getPixel(sf::Vector2u(i, j)) == sf::Color::Black);
}
}
}
SECTION("resize(Vector2, Color)")
{
sf::Image image;
image.resize(sf::Vector2u(10, 10), sf::Color::Red);
CHECK(image.getSize() == sf::Vector2u(10, 10));
CHECK(image.getPixelsPtr() != nullptr);
for (std::uint32_t i = 0; i < 10; ++i)
{
for (std::uint32_t j = 0; j < 10; ++j)
{
CHECK(image.getPixel(sf::Vector2u(i, j)) == sf::Color::Red);
}
}
}
SECTION("resize(Vector2, std::uint8_t*)")
{
// 10 x 10, with 4 colour channels array
std::array<std::uint8_t, 400> pixels{};
for (std::size_t i = 0; i < pixels.size(); i += 4)
{
pixels[i] = 255; // r
pixels[i + 1] = 0; // g
pixels[i + 2] = 0; // b
pixels[i + 3] = 255; // a
}
sf::Image image;
image.resize(sf::Vector2u(10, 10), pixels.data());
CHECK(image.getSize() == sf::Vector2u(10, 10));
CHECK(image.getPixelsPtr() != nullptr);
for (std::uint32_t i = 0; i < 10; ++i)
{
for (std::uint32_t j = 0; j < 10; ++j)
{
CHECK(image.getPixel(sf::Vector2u(i, j)) == sf::Color::Red);
}
}
}
}
SECTION("loadFromFile()")
{
sf::Image image;
SECTION("Invalid file")
{
CHECK(!sf::Image::loadFromFile("."));
CHECK(!sf::Image::loadFromFile("this/does/not/exist.jpg"));
CHECK(!image.loadFromFile("."));
CHECK(!image.loadFromFile("this/does/not/exist.jpg"));
CHECK(image.getSize() == sf::Vector2u(0, 0));
CHECK(image.getPixelsPtr() == nullptr);
}
SECTION("Successful load")
{
std::optional<sf::Image> image;
SECTION("bmp")
{
image = sf::Image::loadFromFile("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));
REQUIRE(image.loadFromFile("Graphics/sfml-logo-big.bmp"));
CHECK(image.getPixel({0, 0}) == sf::Color::White);
CHECK(image.getPixel({200, 150}) == sf::Color(144, 208, 62));
}
SECTION("png")
{
image = sf::Image::loadFromFile("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));
REQUIRE(image.loadFromFile("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));
}
SECTION("jpg")
{
image = sf::Image::loadFromFile("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));
REQUIRE(image.loadFromFile("Graphics/sfml-logo-big.jpg"));
CHECK(image.getPixel({0, 0}) == sf::Color::White);
CHECK(image.getPixel({200, 150}) == sf::Color(144, 208, 62));
}
SECTION("gif")
{
image = sf::Image::loadFromFile("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));
REQUIRE(image.loadFromFile("Graphics/sfml-logo-big.gif"));
CHECK(image.getPixel({0, 0}) == sf::Color::White);
CHECK(image.getPixel({200, 150}) == sf::Color(146, 210, 62));
}
SECTION("psd")
{
image = sf::Image::loadFromFile("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));
REQUIRE(image.loadFromFile("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);
CHECK(image.getSize() == sf::Vector2u(1001, 304));
CHECK(image.getPixelsPtr() != nullptr);
}
}
SECTION("loadFromMemory()")
{
sf::Image image;
SECTION("Invalid pointer")
{
CHECK(!sf::Image::loadFromMemory(nullptr, 1));
CHECK(!image.loadFromMemory(nullptr, 1));
}
SECTION("Invalid size")
{
const std::byte testByte{0xAB};
CHECK(!sf::Image::loadFromMemory(&testByte, 0));
CHECK(!image.loadFromMemory(&testByte, 0));
}
SECTION("Failed load")
@ -162,13 +231,19 @@ TEST_CASE("[Graphics] sf::Image")
memory = {1, 2, 3, 4};
}
CHECK(!sf::Image::loadFromMemory(memory.data(), memory.size()));
CHECK(!image.loadFromMemory(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::loadFromMemory(memory.data(), memory.size()).value();
const auto memory = []()
{
sf::Image savedImage;
savedImage.resize({24, 24}, sf::Color::Green);
return savedImage.saveToMemory("png").value();
}();
CHECK(image.loadFromMemory(memory.data(), memory.size()));
CHECK(image.getSize() == sf::Vector2u(24, 24));
CHECK(image.getPixelsPtr() != nullptr);
CHECK(image.getPixel({0, 0}) == sf::Color::Green);
@ -178,8 +253,127 @@ TEST_CASE("[Graphics] sf::Image")
SECTION("loadFromStream()")
{
auto stream = sf::FileInputStream::open("Graphics/sfml-logo-big.png").value();
const auto image = sf::Image::loadFromStream(stream).value();
sf::Image image;
sf::FileInputStream stream;
SECTION("Invalid stream")
{
CHECK(!image.loadFromStream(stream));
}
SECTION("Successful load")
{
CHECK(stream.open("Graphics/sfml-logo-big.png"));
REQUIRE(image.loadFromStream(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("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));
@ -232,7 +426,7 @@ TEST_CASE("[Graphics] sf::Image")
// Cannot test JPEG encoding due to it triggering UB in stbiw__jpg_writeBits
const auto loadedImage = sf::Image::loadFromFile(filename).value();
const auto loadedImage = sf::Image::createFromFile(filename).value();
CHECK(loadedImage.getSize() == sf::Vector2u(256, 256));
CHECK(loadedImage.getPixelsPtr() != nullptr);

View File

@ -9,13 +9,20 @@ TEST_CASE("[Graphics] sf::RenderTexture", runDisplayTests())
{
SECTION("Type traits")
{
STATIC_CHECK(!std::is_default_constructible_v<sf::RenderTexture>);
STATIC_CHECK(!std::is_copy_constructible_v<sf::RenderTexture>);
STATIC_CHECK(!std::is_copy_assignable_v<sf::RenderTexture>);
STATIC_CHECK(std::is_nothrow_move_constructible_v<sf::RenderTexture>);
STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::RenderTexture>);
}
SECTION("Construction")
{
const sf::RenderTexture renderTexture;
CHECK(!renderTexture.isSmooth());
CHECK(!renderTexture.isRepeated());
CHECK(renderTexture.getSize() == sf::Vector2u(0, 0));
}
SECTION("create()")
{
CHECK(!sf::RenderTexture::create({1'000'000, 1'000'000}));
@ -37,6 +44,21 @@ TEST_CASE("[Graphics] sf::RenderTexture", runDisplayTests())
CHECK(texture.getNativeHandle() != 0);
}
SECTION("resize()")
{
sf::RenderTexture renderTexture;
CHECK(!renderTexture.resize({1'000'000, 1'000'000}));
CHECK(renderTexture.resize({480, 360}));
CHECK(!renderTexture.isSmooth());
CHECK(!renderTexture.isRepeated());
CHECK(renderTexture.getSize() == sf::Vector2u(480, 360));
CHECK(!renderTexture.isSrgb());
CHECK(renderTexture.resize({360, 480}));
CHECK(renderTexture.getSize() == sf::Vector2u(360, 480));
CHECK(renderTexture.resize({100, 100}, sf::ContextSettings{8 /* depthBits */, 0 /* stencilBits */}));
CHECK(renderTexture.resize({100, 100}, sf::ContextSettings{0 /* depthBits */, 8 /* stencilBits */}));
}
SECTION("getMaximumAntialiasingLevel()")
{
CHECK(sf::RenderTexture::getMaximumAntialiasingLevel() <= 64);

View File

@ -147,11 +147,21 @@ TEST_CASE("[Graphics] sf::Shader (Dummy Implementation)", skipShaderDummyTests()
SECTION("loadFromMemory()")
{
CHECK_FALSE(sf::Shader::loadFromMemory(vertexSource, sf::Shader::Type::Vertex).has_value());
CHECK_FALSE(sf::Shader::loadFromMemory(geometrySource, sf::Shader::Type::Geometry).has_value());
CHECK_FALSE(sf::Shader::loadFromMemory(fragmentSource, sf::Shader::Type::Fragment).has_value());
CHECK_FALSE(sf::Shader::loadFromMemory(vertexSource, fragmentSource).has_value());
CHECK_FALSE(sf::Shader::loadFromMemory(vertexSource, geometrySource, fragmentSource).has_value());
sf::Shader shader;
CHECK_FALSE(shader.loadFromMemory(vertexSource, sf::Shader::Type::Vertex));
CHECK_FALSE(shader.loadFromMemory(geometrySource, sf::Shader::Type::Geometry));
CHECK_FALSE(shader.loadFromMemory(fragmentSource, sf::Shader::Type::Fragment));
CHECK_FALSE(shader.loadFromMemory(vertexSource, 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());
}
}
@ -159,26 +169,49 @@ TEST_CASE("[Graphics] sf::Shader", skipShaderFullTests())
{
SECTION("Type traits")
{
STATIC_CHECK(!std::is_default_constructible_v<sf::Shader>);
STATIC_CHECK(!std::is_copy_constructible_v<sf::Shader>);
STATIC_CHECK(!std::is_copy_assignable_v<sf::Shader>);
STATIC_CHECK(std::is_nothrow_move_constructible_v<sf::Shader>);
STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::Shader>);
}
SECTION("Construction")
{
const sf::Shader shader;
CHECK(shader.getNativeHandle() == 0);
}
SECTION("Move semantics")
{
SECTION("Construction")
{
sf::Shader movedShader = sf::Shader::loadFromFile("Graphics/shader.vert", sf::Shader::Type::Vertex).value();
sf::Shader movedShader;
const sf::Shader shader = std::move(movedShader);
CHECK(shader.getNativeHandle() == 0);
}
SECTION("Assignment")
{
sf::Shader movedShader;
sf::Shader shader;
shader = std::move(movedShader);
CHECK(shader.getNativeHandle() == 0);
}
}
SECTION("Move semantics (create)")
{
SECTION("Construction")
{
sf::Shader movedShader = sf::Shader::createFromFile("Graphics/shader.vert", sf::Shader::Type::Vertex).value();
const sf::Shader shader = std::move(movedShader);
CHECK(shader.getNativeHandle() != 0);
}
SECTION("Assignment")
{
sf::Shader movedShader = sf::Shader::loadFromFile("Graphics/shader.vert", sf::Shader::Type::Vertex).value();
sf::Shader shader = sf::Shader::loadFromFile("Graphics/shader.frag", sf::Shader::Type::Fragment).value();
sf::Shader movedShader = sf::Shader::createFromFile("Graphics/shader.vert", sf::Shader::Type::Vertex).value();
sf::Shader shader = sf::Shader::createFromFile("Graphics/shader.frag", sf::Shader::Type::Fragment).value();
shader = std::move(movedShader);
CHECK(shader.getNativeHandle() != 0);
}
@ -186,16 +219,100 @@ TEST_CASE("[Graphics] sf::Shader", skipShaderFullTests())
SECTION("loadFromFile()")
{
sf::Shader shader;
SECTION("One shader")
{
CHECK(!sf::Shader::loadFromFile("does-not-exist.vert", sf::Shader::Type::Vertex));
CHECK(!shader.loadFromFile("does-not-exist.vert", sf::Shader::Type::Vertex));
const auto vertexShader = sf::Shader::loadFromFile("Graphics/shader.vert", sf::Shader::Type::Vertex);
CHECK(shader.loadFromFile("Graphics/shader.vert", sf::Shader::Type::Vertex) == sf::Shader::isAvailable());
CHECK(static_cast<bool>(shader.getNativeHandle()) == sf::Shader::isAvailable());
CHECK(shader.loadFromFile("Graphics/shader.frag", sf::Shader::Type::Fragment) == sf::Shader::isAvailable());
CHECK(static_cast<bool>(shader.getNativeHandle()) == sf::Shader::isAvailable());
}
SECTION("Two shaders")
{
CHECK(!shader.loadFromFile("does-not-exist.vert", "Graphics/shader.frag"));
CHECK(!shader.loadFromFile("Graphics/shader.vert", "does-not-exist.frag"));
CHECK(shader.loadFromFile("Graphics/shader.vert", "Graphics/shader.frag") == sf::Shader::isAvailable());
CHECK(static_cast<bool>(shader.getNativeHandle()) == sf::Shader::isAvailable());
}
SECTION("Three shaders")
{
CHECK(!shader.loadFromFile("does-not-exist.vert", "Graphics/shader.geom", "Graphics/shader.frag"));
CHECK(!shader.loadFromFile("Graphics/shader.vert", "does-not-exist.geom", "Graphics/shader.frag"));
CHECK(!shader.loadFromFile("Graphics/shader.vert", "Graphics/shader.geom", "does-not-exist.frag"));
CHECK(shader.loadFromFile("Graphics/shader.vert", "Graphics/shader.geom", "Graphics/shader.frag") ==
sf::Shader::isGeometryAvailable());
CHECK(static_cast<bool>(shader.getNativeHandle()) == sf::Shader::isGeometryAvailable());
}
}
SECTION("loadFromMemory()")
{
sf::Shader shader;
CHECK(shader.loadFromMemory(vertexSource, sf::Shader::Type::Vertex) == sf::Shader::isAvailable());
CHECK_FALSE(shader.loadFromMemory(geometrySource, sf::Shader::Type::Geometry));
CHECK(shader.loadFromMemory(fragmentSource, sf::Shader::Type::Fragment) == sf::Shader::isAvailable());
CHECK(shader.loadFromMemory(vertexSource, fragmentSource) == sf::Shader::isAvailable());
CHECK(shader.loadFromMemory(vertexSource, geometrySource, fragmentSource) == sf::Shader::isGeometryAvailable());
CHECK(static_cast<bool>(shader.getNativeHandle()) == sf::Shader::isAvailable());
}
SECTION("loadFromStream()")
{
sf::Shader shader;
sf::FileInputStream vertexShaderStream;
REQUIRE(vertexShaderStream.open("Graphics/shader.vert"));
sf::FileInputStream fragmentShaderStream;
REQUIRE(fragmentShaderStream.open("Graphics/shader.frag"));
sf::FileInputStream geometryShaderStream;
REQUIRE(geometryShaderStream.open("Graphics/shader.geom"));
sf::FileInputStream emptyStream;
SECTION("One shader")
{
REQUIRE(!shader.loadFromStream(emptyStream, sf::Shader::Type::Vertex));
REQUIRE(shader.loadFromStream(vertexShaderStream, sf::Shader::Type::Vertex) == sf::Shader::isAvailable());
REQUIRE(shader.loadFromStream(fragmentShaderStream, sf::Shader::Type::Fragment) == sf::Shader::isAvailable());
}
SECTION("Two shaders")
{
REQUIRE(!shader.loadFromStream(emptyStream, fragmentShaderStream));
REQUIRE(!shader.loadFromStream(vertexShaderStream, emptyStream));
REQUIRE(shader.loadFromStream(vertexShaderStream, fragmentShaderStream) == sf::Shader::isAvailable());
}
SECTION("Three shaders")
{
REQUIRE(!shader.loadFromStream(emptyStream, geometryShaderStream, fragmentShaderStream));
REQUIRE(!shader.loadFromStream(vertexShaderStream, emptyStream, fragmentShaderStream));
REQUIRE(!shader.loadFromStream(vertexShaderStream, geometryShaderStream, emptyStream));
REQUIRE(shader.loadFromStream(vertexShaderStream, geometryShaderStream, fragmentShaderStream) ==
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::loadFromFile("Graphics/shader.frag", sf::Shader::Type::Fragment);
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());
@ -203,10 +320,10 @@ TEST_CASE("[Graphics] sf::Shader", skipShaderFullTests())
SECTION("Two shaders")
{
CHECK(!sf::Shader::loadFromFile("does-not-exist.vert", "Graphics/shader.frag"));
CHECK(!sf::Shader::loadFromFile("Graphics/shader.vert", "does-not-exist.frag"));
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::loadFromFile("Graphics/shader.vert", "Graphics/shader.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());
@ -214,11 +331,11 @@ TEST_CASE("[Graphics] sf::Shader", skipShaderFullTests())
SECTION("Three shaders")
{
CHECK(!sf::Shader::loadFromFile("does-not-exist.vert", "Graphics/shader.geom", "Graphics/shader.frag"));
CHECK(!sf::Shader::loadFromFile("Graphics/shader.vert", "does-not-exist.geom", "Graphics/shader.frag"));
CHECK(!sf::Shader::loadFromFile("Graphics/shader.vert", "Graphics/shader.geom", "does-not-exist.frag"));
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::loadFromFile("Graphics/shader.vert",
const auto shader = sf::Shader::createFromFile("Graphics/shader.vert",
"Graphics/shader.geom",
"Graphics/shader.frag");
CHECK(shader.has_value() == sf::Shader::isGeometryAvailable());
@ -227,52 +344,53 @@ TEST_CASE("[Graphics] sf::Shader", skipShaderFullTests())
}
}
SECTION("loadFromMemory()")
SECTION("createFromMemory()")
{
CHECK(sf::Shader::loadFromMemory(vertexSource, sf::Shader::Type::Vertex).has_value() == sf::Shader::isAvailable());
CHECK(!sf::Shader::loadFromMemory(geometrySource, sf::Shader::Type::Geometry));
CHECK(sf::Shader::loadFromMemory(fragmentSource, sf::Shader::Type::Fragment).has_value() ==
CHECK(sf::Shader::createFromMemory(vertexSource, sf::Shader::Type::Vertex).has_value() ==
sf::Shader::isAvailable());
CHECK(sf::Shader::loadFromMemory(vertexSource, fragmentSource).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::loadFromMemory(vertexSource, geometrySource, fragmentSource);
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("loadFromStream()")
SECTION("createFromStream()")
{
auto vertexShaderStream = sf::FileInputStream::open("Graphics/shader.vert").value();
auto fragmentShaderStream = sf::FileInputStream::open("Graphics/shader.frag").value();
auto geometryShaderStream = sf::FileInputStream::open("Graphics/shader.geom").value();
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::open("Graphics/invalid_shader.vert").value();
auto emptyStream = sf::FileInputStream::create("Graphics/invalid_shader.vert").value();
SECTION("One shader")
{
CHECK(!sf::Shader::loadFromStream(emptyStream, sf::Shader::Type::Vertex));
CHECK(sf::Shader::loadFromStream(vertexShaderStream, sf::Shader::Type::Vertex).has_value() ==
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::loadFromStream(fragmentShaderStream, sf::Shader::Type::Fragment).has_value() ==
CHECK(sf::Shader::createFromStream(fragmentShaderStream, sf::Shader::Type::Fragment).has_value() ==
sf::Shader::isAvailable());
}
SECTION("Two shaders")
{
CHECK(!sf::Shader::loadFromStream(emptyStream, fragmentShaderStream));
CHECK(!sf::Shader::loadFromStream(vertexShaderStream, emptyStream));
CHECK(sf::Shader::loadFromStream(vertexShaderStream, fragmentShaderStream).has_value() ==
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::loadFromStream(emptyStream, geometryShaderStream, fragmentShaderStream));
CHECK(!sf::Shader::loadFromStream(vertexShaderStream, emptyStream, fragmentShaderStream));
CHECK(!sf::Shader::loadFromStream(vertexShaderStream, geometryShaderStream, emptyStream));
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::loadFromStream(vertexShaderStream, geometryShaderStream, fragmentShaderStream);
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

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

View File

@ -15,7 +15,6 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
{
SECTION("Type traits")
{
STATIC_CHECK(!std::is_default_constructible_v<sf::Texture>);
STATIC_CHECK(std::is_copy_constructible_v<sf::Texture>);
STATIC_CHECK(std::is_copy_assignable_v<sf::Texture>);
STATIC_CHECK(std::is_nothrow_move_constructible_v<sf::Texture>);
@ -23,7 +22,43 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
STATIC_CHECK(std::is_nothrow_swappable_v<sf::Texture>);
}
SECTION("Construction")
{
const sf::Texture texture;
CHECK(texture.getSize() == sf::Vector2u());
CHECK(!texture.isSmooth());
CHECK(!texture.isSrgb());
CHECK(!texture.isRepeated());
CHECK(texture.getNativeHandle() == 0);
}
SECTION("Move semantics")
{
SECTION("Construction")
{
sf::Texture movedTexture;
const sf::Texture texture = std::move(movedTexture);
CHECK(texture.getSize() == sf::Vector2u());
CHECK(!texture.isSmooth());
CHECK(!texture.isSrgb());
CHECK(!texture.isRepeated());
CHECK(texture.getNativeHandle() == 0);
}
SECTION("Assignment")
{
sf::Texture movedTexture;
sf::Texture texture;
texture = std::move(movedTexture);
CHECK(texture.getSize() == sf::Vector2u());
CHECK(!texture.isSmooth());
CHECK(!texture.isSrgb());
CHECK(!texture.isRepeated());
CHECK(texture.getNativeHandle() == 0);
}
}
SECTION("Move semantics (create)")
{
SECTION("Construction")
{
@ -72,9 +107,35 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
}
}
SECTION("resize()")
{
sf::Texture texture;
SECTION("At least one zero dimension")
{
CHECK(!texture.resize({}));
CHECK(!texture.resize({0, 1}));
CHECK(!texture.resize({1, 0}));
}
SECTION("Valid size")
{
CHECK(texture.resize({100, 100}));
CHECK(texture.getSize() == sf::Vector2u(100, 100));
CHECK(texture.getNativeHandle() != 0);
}
SECTION("Too large")
{
CHECK(!texture.resize({100'000, 100'000}));
CHECK(!texture.resize({1'000'000, 1'000'000}));
}
}
SECTION("loadFromFile()")
{
const auto texture = sf::Texture::loadFromFile("Graphics/sfml-logo-big.png").value();
sf::Texture texture;
REQUIRE(texture.loadFromFile("Graphics/sfml-logo-big.png"));
CHECK(texture.getSize() == sf::Vector2u(1001, 304));
CHECK(!texture.isSmooth());
CHECK(!texture.isSrgb());
@ -85,7 +146,8 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
SECTION("loadFromMemory()")
{
const auto memory = loadIntoMemory("Graphics/sfml-logo-big.png");
const auto texture = sf::Texture::loadFromMemory(memory.data(), memory.size()).value();
sf::Texture texture;
REQUIRE(texture.loadFromMemory(memory.data(), memory.size()));
CHECK(texture.getSize() == sf::Vector2u(1001, 304));
CHECK(!texture.isSmooth());
CHECK(!texture.isSrgb());
@ -95,8 +157,10 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
SECTION("loadFromStream()")
{
auto stream = sf::FileInputStream::open("Graphics/sfml-logo-big.png").value();
const auto texture = sf::Texture::loadFromStream(stream).value();
sf::Texture texture;
sf::FileInputStream stream;
REQUIRE(stream.open("Graphics/sfml-logo-big.png"));
REQUIRE(texture.loadFromStream(stream));
CHECK(texture.getSize() == sf::Vector2u(1001, 304));
CHECK(!texture.isSmooth());
CHECK(!texture.isSrgb());
@ -105,6 +169,76 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
}
SECTION("loadFromImage()")
{
SECTION("Empty image")
{
const sf::Image image;
sf::Texture texture;
REQUIRE(!texture.loadFromImage(image));
REQUIRE(!texture.loadFromImage(image, false, {{0, 0}, {1, 1}}));
}
SECTION("Subarea of image")
{
sf::Image image;
image.resize(sf::Vector2u(10, 15));
sf::Texture texture;
SECTION("Non-truncated area")
{
REQUIRE(texture.loadFromImage(image, false, {{0, 0}, {5, 10}}));
CHECK(texture.getSize() == sf::Vector2u(5, 10));
}
SECTION("Truncated area (negative position)")
{
REQUIRE(texture.loadFromImage(image, false, {{-5, -5}, {4, 8}}));
CHECK(texture.getSize() == sf::Vector2u(4, 8));
}
SECTION("Truncated area (width/height too big)")
{
REQUIRE(texture.loadFromImage(image, false, {{5, 5}, {12, 18}}));
CHECK(texture.getSize() == sf::Vector2u(5, 10));
}
CHECK(texture.getNativeHandle() != 0);
}
}
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")
{
@ -112,21 +246,21 @@ TEST_CASE("[Graphics] sf::Texture", runDisplayTests())
SECTION("Non-truncated area")
{
const auto texture = sf::Texture::loadFromImage(image, false, {{0, 0}, {5, 10}}).value();
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::loadFromImage(image, false, {{-5, -5}, {4, 8}}).value();
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::loadFromImage(image, false, {{5, 5}, {12, 18}}).value();
const auto texture = sf::Texture::createFromImage(image, false, {{5, 5}, {12, 18}}).value();
CHECK(texture.getSize() == sf::Vector2u(5, 10));
CHECK(texture.getNativeHandle() != 0);
}

View File

@ -66,13 +66,21 @@ TEST_CASE("[System] sf::FileInputStream")
SECTION("Type traits")
{
STATIC_CHECK(!std::is_default_constructible_v<sf::FileInputStream>);
STATIC_CHECK(!std::is_copy_constructible_v<sf::FileInputStream>);
STATIC_CHECK(!std::is_copy_assignable_v<sf::FileInputStream>);
STATIC_CHECK(std::is_nothrow_move_constructible_v<sf::FileInputStream>);
STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::FileInputStream>);
}
SECTION("Default constructor")
{
sf::FileInputStream fileInputStream;
CHECK(fileInputStream.read(nullptr, 0) == std::nullopt);
CHECK(fileInputStream.seek(0) == std::nullopt);
CHECK(fileInputStream.tell() == std::nullopt);
CHECK(fileInputStream.getSize() == std::nullopt);
}
const TemporaryFile temporaryFile("Hello world");
char buffer[32];
@ -80,7 +88,7 @@ TEST_CASE("[System] sf::FileInputStream")
{
SECTION("Move constructor")
{
auto movedFileInputStream = sf::FileInputStream::open(temporaryFile.getPath()).value();
auto movedFileInputStream = sf::FileInputStream::create(temporaryFile.getPath()).value();
sf::FileInputStream fileInputStream = std::move(movedFileInputStream);
CHECK(fileInputStream.read(buffer, 6) == 6);
CHECK(fileInputStream.tell() == 6);
@ -90,9 +98,9 @@ TEST_CASE("[System] sf::FileInputStream")
SECTION("Move assignment")
{
auto movedFileInputStream = sf::FileInputStream::open(temporaryFile.getPath()).value();
auto movedFileInputStream = sf::FileInputStream::create(temporaryFile.getPath()).value();
const TemporaryFile temporaryFile2("Hello world the sequel");
auto fileInputStream = sf::FileInputStream::open(temporaryFile2.getPath()).value();
auto fileInputStream = sf::FileInputStream::create(temporaryFile2.getPath()).value();
fileInputStream = std::move(movedFileInputStream);
CHECK(fileInputStream.read(buffer, 6) == 6);
CHECK(fileInputStream.tell() == 6);
@ -101,9 +109,21 @@ TEST_CASE("[System] sf::FileInputStream")
}
}
SECTION("Temporary file stream")
SECTION("Temporary file stream open")
{
auto fileInputStream = sf::FileInputStream::open(temporaryFile.getPath()).value();
sf::FileInputStream fileInputStream;
REQUIRE(fileInputStream.open(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("Temporary file stream create")
{
auto fileInputStream = sf::FileInputStream::create(temporaryFile.getPath()).value();
CHECK(fileInputStream.read(buffer, 5) == 5);
CHECK(fileInputStream.tell() == 5);
CHECK(fileInputStream.getSize() == 11);

View File

@ -21,27 +21,27 @@ TEST_CASE("[Window] sf::Cursor", runDisplayTests())
{
static constexpr std::array<std::uint8_t, 4> pixels{};
CHECK(!sf::Cursor::loadFromPixels(nullptr, {}, {}));
CHECK(!sf::Cursor::loadFromPixels(pixels.data(), {0, 1}, {}));
CHECK(!sf::Cursor::loadFromPixels(pixels.data(), {1, 0}, {}));
CHECK(sf::Cursor::loadFromPixels(pixels.data(), {1, 1}, {}));
CHECK(!sf::Cursor::createFromPixels(nullptr, {}, {}));
CHECK(!sf::Cursor::createFromPixels(pixels.data(), {0, 1}, {}));
CHECK(!sf::Cursor::createFromPixels(pixels.data(), {1, 0}, {}));
CHECK(sf::Cursor::createFromPixels(pixels.data(), {1, 1}, {}));
}
SECTION("loadFromSystem()")
{
CHECK(sf::Cursor::loadFromSystem(sf::Cursor::Type::Hand));
CHECK(sf::Cursor::loadFromSystem(sf::Cursor::Type::SizeHorizontal));
CHECK(sf::Cursor::loadFromSystem(sf::Cursor::Type::SizeVertical));
CHECK(sf::Cursor::loadFromSystem(sf::Cursor::Type::SizeLeft));
CHECK(sf::Cursor::loadFromSystem(sf::Cursor::Type::SizeRight));
CHECK(sf::Cursor::loadFromSystem(sf::Cursor::Type::SizeTop));
CHECK(sf::Cursor::loadFromSystem(sf::Cursor::Type::SizeBottom));
CHECK(sf::Cursor::loadFromSystem(sf::Cursor::Type::SizeTopLeft));
CHECK(sf::Cursor::loadFromSystem(sf::Cursor::Type::SizeTopRight));
CHECK(sf::Cursor::loadFromSystem(sf::Cursor::Type::SizeBottomLeft));
CHECK(sf::Cursor::loadFromSystem(sf::Cursor::Type::SizeBottomRight));
CHECK(sf::Cursor::loadFromSystem(sf::Cursor::Type::Cross));
CHECK(sf::Cursor::loadFromSystem(sf::Cursor::Type::Help));
CHECK(sf::Cursor::loadFromSystem(sf::Cursor::Type::NotAllowed));
CHECK(sf::Cursor::createFromSystem(sf::Cursor::Type::Hand));
CHECK(sf::Cursor::createFromSystem(sf::Cursor::Type::SizeHorizontal));
CHECK(sf::Cursor::createFromSystem(sf::Cursor::Type::SizeVertical));
CHECK(sf::Cursor::createFromSystem(sf::Cursor::Type::SizeLeft));
CHECK(sf::Cursor::createFromSystem(sf::Cursor::Type::SizeRight));
CHECK(sf::Cursor::createFromSystem(sf::Cursor::Type::SizeTop));
CHECK(sf::Cursor::createFromSystem(sf::Cursor::Type::SizeBottom));
CHECK(sf::Cursor::createFromSystem(sf::Cursor::Type::SizeTopLeft));
CHECK(sf::Cursor::createFromSystem(sf::Cursor::Type::SizeTopRight));
CHECK(sf::Cursor::createFromSystem(sf::Cursor::Type::SizeBottomLeft));
CHECK(sf::Cursor::createFromSystem(sf::Cursor::Type::SizeBottomRight));
CHECK(sf::Cursor::createFromSystem(sf::Cursor::Type::Cross));
CHECK(sf::Cursor::createFromSystem(sf::Cursor::Type::Help));
CHECK(sf::Cursor::createFromSystem(sf::Cursor::Type::NotAllowed));
}
}