mirror of
https://github.com/SFML/SFML.git
synced 2024-11-25 04:41:05 +08:00
(Re-)Introduce default constructors and load/open member functions for resource objects that can be reused.
This commit is contained in:
parent
6b4da70e15
commit
698f265277
@ -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);
|
||||
|
@ -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};
|
||||
};
|
||||
|
@ -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};
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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'
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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 (...)
|
||||
/// {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
////////////////////////////////////////////////////////////
|
||||
|
@ -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);
|
||||
/// ...
|
||||
///
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
////////////////////////////////////////////////////////////
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
///
|
||||
|
@ -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
|
||||
///
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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>);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user