From c5276ff30a3e51ac034a398b789010d4503e5e72 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Sun, 17 Jul 2011 12:21:47 +0200 Subject: [PATCH] Added a new InputStream interface, and LoadFromStream functions to resource classes --- include/SFML/Audio/Music.hpp | 30 +++- include/SFML/Audio/SoundBuffer.hpp | 42 +++++- include/SFML/Graphics/Font.hpp | 34 ++++- include/SFML/Graphics/Image.hpp | 24 ++- include/SFML/Graphics/Shader.hpp | 21 ++- include/SFML/System.hpp | 1 + include/SFML/System/InputStream.hpp | 148 +++++++++++++++++++ src/SFML/Audio/Music.cpp | 59 +++++--- src/SFML/Audio/SoundBuffer.cpp | 89 ++++++------ src/SFML/Audio/SoundFile.cpp | 217 ++++++++++++++++++---------- src/SFML/Audio/SoundFile.hpp | 49 +++++-- src/SFML/Graphics/Font.cpp | 97 ++++++++++++- src/SFML/Graphics/Image.cpp | 22 +++ src/SFML/Graphics/ImageLoader.cpp | 62 ++++++++ src/SFML/Graphics/ImageLoader.hpp | 17 ++- src/SFML/Graphics/Shader.cpp | 16 +- 16 files changed, 736 insertions(+), 192 deletions(-) create mode 100644 include/SFML/System/InputStream.hpp diff --git a/include/SFML/Audio/Music.hpp b/include/SFML/Audio/Music.hpp index 423c359a..76454071 100644 --- a/include/SFML/Audio/Music.hpp +++ b/include/SFML/Audio/Music.hpp @@ -41,6 +41,8 @@ namespace priv class SoundFile; } +class InputStream; + //////////////////////////////////////////////////////////// /// \brief Streamed music played from an audio file /// @@ -74,7 +76,7 @@ public : /// /// \return True if loading succeeded, false if it failed /// - /// \see OpenFromMemory + /// \see OpenFromMemory, OpenFromStream /// //////////////////////////////////////////////////////////// bool OpenFromFile(const std::string& filename); @@ -93,11 +95,29 @@ public : /// /// \return True if loading succeeded, false if it failed /// - /// \see OpenFromFile + /// \see OpenFromFile, OpenFromStream /// //////////////////////////////////////////////////////////// bool OpenFromMemory(const void* data, std::size_t sizeInBytes); + //////////////////////////////////////////////////////////// + /// \brief Open a music from an audio file in a custom stream + /// + /// This function doesn't start playing the music (call Play() + /// to do so). + /// Here is a complete list of all the supported audio formats: + /// ogg, wav, flac, aiff, au, raw, paf, svx, nist, voc, ircam, + /// w64, mat4, mat5 pvf, htk, sds, avr, sd2, caf, wve, mpc2k, rf64. + /// + /// \param stream Source stream to read from + /// + /// \return True if loading succeeded, false if it failed + /// + /// \see OpenFromFile, OpenFromMemory + /// + //////////////////////////////////////////////////////////// + bool OpenFromStream(InputStream& stream); + //////////////////////////////////////////////////////////// /// \brief Get the total duration of the music /// @@ -131,6 +151,12 @@ protected : private : + //////////////////////////////////////////////////////////// + /// \brief Initialize the internal state after loading a new music + /// + //////////////////////////////////////////////////////////// + void Initialize(); + //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// diff --git a/include/SFML/Audio/SoundBuffer.hpp b/include/SFML/Audio/SoundBuffer.hpp index f06841ad..5c7d65e8 100644 --- a/include/SFML/Audio/SoundBuffer.hpp +++ b/include/SFML/Audio/SoundBuffer.hpp @@ -37,7 +37,13 @@ namespace sf { +namespace priv +{ + class SoundFile; +} + class Sound; +class InputStream; //////////////////////////////////////////////////////////// /// \brief Storage for audio samples defining a sound @@ -78,7 +84,7 @@ public : /// /// \return True if loading succeeded, false if it failed /// - /// \see LoadFromMemory, LoadFromSamples, SaveToFile + /// \see LoadFromMemory, LoadFromStream, LoadFromSamples, SaveToFile /// //////////////////////////////////////////////////////////// bool LoadFromFile(const std::string& filename); @@ -95,11 +101,27 @@ public : /// /// \return True if loading succeeded, false if it failed /// - /// \see LoadFromFile, LoadFromSamples, SaveToFile + /// \see LoadFromFile, LoadFromStream, LoadFromSamples /// //////////////////////////////////////////////////////////// bool LoadFromMemory(const void* data, std::size_t sizeInBytes); + //////////////////////////////////////////////////////////// + /// \brief Load the sound buffer from a custom stream + /// + /// Here is a complete list of all the supported audio formats: + /// ogg, wav, flac, aiff, au, raw, paf, svx, nist, voc, ircam, + /// w64, mat4, mat5 pvf, htk, sds, avr, sd2, caf, wve, mpc2k, rf64. + /// + /// \param stream Source stream to read from + /// + /// \return True if loading succeeded, false if it failed + /// + /// \see LoadFromFile, LoadFromMemory, LoadFromSamples + /// + //////////////////////////////////////////////////////////// + bool LoadFromStream(InputStream& stream); + //////////////////////////////////////////////////////////// /// \brief Load the sound buffer from an array of audio samples /// @@ -212,6 +234,16 @@ private : friend class Sound; + //////////////////////////////////////////////////////////// + /// \brief Initialize the internal state after loading a new sound + /// + /// \param file Sound file providing access to the new loaded sound + /// + /// \return True on succesful initialization, false on failure + /// + //////////////////////////////////////////////////////////// + bool Initialize(priv::SoundFile& file); + //////////////////////////////////////////////////////////// /// \brief Update the internal buffer with the cached audio samples /// @@ -273,9 +305,9 @@ private : /// a sf::Image. /// /// A sound buffer can be loaded from a file (see LoadFromFile() -/// for the complete list of supported formats), from memory -/// or directly from an array of samples. It can also be saved -/// back to a file. +/// 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. /// /// Sound buffers alone are not very useful: they hold the audio data /// but cannot be played. To do so, you need to use the sf::Sound class, diff --git a/include/SFML/Graphics/Font.hpp b/include/SFML/Graphics/Font.hpp index b6555703..37945f9e 100644 --- a/include/SFML/Graphics/Font.hpp +++ b/include/SFML/Graphics/Font.hpp @@ -41,6 +41,8 @@ namespace sf { +class InputStream; + //////////////////////////////////////////////////////////// /// \brief Class for loading and manipulating character fonts /// @@ -86,7 +88,7 @@ public : /// /// \return True if loading succeeded, false if it failed /// - /// \see LoadFromMemory + /// \see LoadFromMemory, LoadFromStream /// //////////////////////////////////////////////////////////// bool LoadFromFile(const std::string& filename); @@ -96,9 +98,6 @@ public : /// /// The supported font formats are: TrueType, Type 1, CFF, /// OpenType, SFNT, X11 PCF, Windows FNT, BDF, PFR and Type 42. - /// Note that this function know 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 buffer pointed by \a data has to remain /// valid as long as the font is used. @@ -108,11 +107,29 @@ public : /// /// \return True if loading succeeded, false if it failed /// - /// \see LoadFromFile + /// \see LoadFromFile, LoadFromStream /// //////////////////////////////////////////////////////////// bool LoadFromMemory(const void* data, std::size_t sizeInBytes); + //////////////////////////////////////////////////////////// + /// \brief Load 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. + /// + /// \param stream Source stream to read from + /// + /// \return True if loading succeeded, false if it failed + /// + /// \see LoadFromFile, LoadFromMemory + /// + //////////////////////////////////////////////////////////// + bool LoadFromStream(InputStream& stream); + //////////////////////////////////////////////////////////// /// \brief Retrieve a glyph of the font /// @@ -278,6 +295,7 @@ private : //////////////////////////////////////////////////////////// void* myLibrary; ///< Pointer to the internal library interface (it is typeless to avoid exposing implementation details) void* myFace; ///< Pointer to the internal font face (it is typeless to avoid exposing implementation details) + void* myStreamRec; ///< Pointer to the stream rec instance (it is typeless to avoid exposing implementation details) int* myRefCount; ///< Reference counter used by implicit sharing mutable PageTable myPages; ///< Table containing the glyphs pages by character size mutable std::vector myPixelBuffer; ///< Pixel buffer holding a glyph's pixels before being written to the texture @@ -293,9 +311,9 @@ private : /// \class sf::Font /// \ingroup graphics /// -/// Fonts can be loaded from a file or from memory, from -/// the most common types of fonts. See the LoadFromFile -/// function for the complete list of supported formats. +/// Fonts can be loaded from a file, from memory or from a custom +/// stream, and supports the most common types of fonts. See +/// the LoadFromFile function for the complete list of supported formats. /// /// Once it is loaded, a sf::Font instance provides three /// types of informations about the font: diff --git a/include/SFML/Graphics/Image.hpp b/include/SFML/Graphics/Image.hpp index 45861f56..902ffa90 100644 --- a/include/SFML/Graphics/Image.hpp +++ b/include/SFML/Graphics/Image.hpp @@ -41,6 +41,7 @@ namespace sf class Renderer; class RenderImage; class RenderWindow; +class InputStream; //////////////////////////////////////////////////////////// /// \brief Class for loading, manipulating and saving images @@ -86,7 +87,7 @@ public : /// /// \return True if loading was successful /// - /// \see LoadFromMemory, LoadFromPixels, SaveToFile + /// \see LoadFromMemory, LoadFromStream, LoadFromPixels, SaveToFile /// //////////////////////////////////////////////////////////// bool LoadFromFile(const std::string& filename); @@ -106,11 +107,30 @@ public : /// /// \return True if loading was successful /// - /// \see LoadFromFile, LoadFromPixels, SaveToFile + /// \see LoadFromFile, LoadFromStream, LoadFromPixels /// //////////////////////////////////////////////////////////// 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 and pic. Some format options are not supported, + /// like progressive jpeg. + /// The maximum size for an image depends on the graphics + /// driver and can be retrieve with the GetMaximumSize function. + /// 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, LoadFromPixels + /// + //////////////////////////////////////////////////////////// + bool LoadFromStream(InputStream& stream); + //////////////////////////////////////////////////////////// /// \brief Load the image from an array of pixels /// diff --git a/include/SFML/Graphics/Shader.hpp b/include/SFML/Graphics/Shader.hpp index 9f947e0e..ce294b4b 100644 --- a/include/SFML/Graphics/Shader.hpp +++ b/include/SFML/Graphics/Shader.hpp @@ -84,7 +84,7 @@ public : /// /// \return True if loading succeeded, false if it failed /// - /// \see LoadFromMemory + /// \see LoadFromMemory, LoadFromStream /// //////////////////////////////////////////////////////////// bool LoadFromFile(const std::string& filename); @@ -101,11 +101,28 @@ public : /// /// \return True if loading succeeded, false if it failed /// - /// \see LoadFromFile + /// \see LoadFromFile, LoadFromStream /// //////////////////////////////////////////////////////////// bool LoadFromMemory(const std::string& shader); + //////////////////////////////////////////////////////////// + /// \brief Load the shader from a custom stream + /// + /// The source code must be a valid fragment 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 + /// + /// \return True if loading succeeded, false if it failed + /// + /// \see LoadFromFile, LoadFromMemory + /// + //////////////////////////////////////////////////////////// + bool LoadFromStream(InputStream& stream); + //////////////////////////////////////////////////////////// /// \brief Change a float parameter of the shader /// diff --git a/include/SFML/System.hpp b/include/SFML/System.hpp index 8f4cc681..9b360e11 100644 --- a/include/SFML/System.hpp +++ b/include/SFML/System.hpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/include/SFML/System/InputStream.hpp b/include/SFML/System/InputStream.hpp new file mode 100644 index 00000000..10fe59cf --- /dev/null +++ b/include/SFML/System/InputStream.hpp @@ -0,0 +1,148 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_INPUTSTREAM_HPP +#define SFML_INPUTSTREAM_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Abstract class for custom file input streams +/// +//////////////////////////////////////////////////////////// +class SFML_API InputStream +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Virtual destructor + /// + //////////////////////////////////////////////////////////// + virtual ~InputStream() {} + + //////////////////////////////////////////////////////////// + /// \brief Read data from the stream + /// + /// \param data Buffer where to copy the read data + /// \param size Desired number of bytes to read + /// + /// \return The number of bytes actually read + /// + //////////////////////////////////////////////////////////// + virtual Int64 Read(char* data, Int64 size) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Change the current reading position + /// + /// \param position The position to seek to, from the beginning + /// + /// \return The position actually seeked to, or -1 on error + /// + //////////////////////////////////////////////////////////// + virtual Int64 Seek(Int64 position) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Return the current reading position in the stream + /// + /// \return The current position, or -1 on error. + /// + //////////////////////////////////////////////////////////// + virtual Int64 GetPosition() = 0; + + //////////////////////////////////////////////////////////// + /// \brief Return the size of the stream + /// + /// \return The total number of bytes available in the stream, or -1 on error + /// + //////////////////////////////////////////////////////////// + virtual Int64 GetSize() = 0; +}; + +} // namespace sf + + +#endif // SFML_INPUTSTREAM_HPP + + +//////////////////////////////////////////////////////////// +/// \class sf::InputStream +/// \ingroup system +/// +/// This class allows users to define their own file input sources +/// from which SFML can load resources. +/// +/// SFML resource classes like sf::Image and +/// sf::SoundBuffer provide LoadFromFile and LoadFromMemory 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. +/// +/// Usage example: +/// \code +/// // custom stream class that reads from inside a zip file +/// class ZipStream : public sf::InputStream +/// { +/// public : +/// +/// ZipStream(std::string archive); +/// +/// bool Open(std::string filename); +/// +/// Int64 Read(char* data, Int64 size); +/// +/// Int64 Seek(Int64 position); +/// +/// Int64 GetPosition(); +/// +/// Int64 GetSize(); +/// +/// private : +/// +/// ... +/// }; +/// +/// // now you can load images... +/// sf::Image image; +/// ZipStream stream("resources.zip"); +/// stream.Open("images/img.png"); +/// image.LoadFromStream(stream); +/// +/// // musics... +/// sf::Music music; +/// ZipStream stream("resources.zip"); +/// stream.Open("musics/msc.ogg"); +/// music.OpenFromStream(stream); +/// +/// // etc. +/// \endcode +/// +//////////////////////////////////////////////////////////// diff --git a/src/SFML/Audio/Music.cpp b/src/SFML/Audio/Music.cpp index 5b8be796..747d79c8 100644 --- a/src/SFML/Audio/Music.cpp +++ b/src/SFML/Audio/Music.cpp @@ -60,21 +60,12 @@ bool Music::OpenFromFile(const std::string& filename) // First stop the music if it was already running Stop(); - // Create the sound file implementation, and open it in read mode + // Open the underlying sound file if (!myFile->OpenRead(filename)) - { - Err() << "Failed to open \"" << filename << "\" for reading" << std::endl; return false; - } - // Compute the duration - myDuration = static_cast(1000 * myFile->GetSamplesCount() / myFile->GetSampleRate() / myFile->GetChannelsCount()); - - // Resize the internal buffer so that it can contain 1 second of audio samples - mySamples.resize(myFile->GetSampleRate() * myFile->GetChannelsCount()); - - // Initialize the stream - Initialize(myFile->GetChannelsCount(), myFile->GetSampleRate()); + // Perform common initializations + Initialize(); return true; } @@ -86,21 +77,29 @@ bool Music::OpenFromMemory(const void* data, std::size_t sizeInBytes) // First stop the music if it was already running Stop(); - // Create the sound file implementation, and open it in read mode + // Open the underlying sound file if (!myFile->OpenRead(data, sizeInBytes)) - { - Err() << "Failed to open music from memory for reading" << std::endl; return false; - } - // Compute the duration - myDuration = static_cast(1000 * myFile->GetSamplesCount() / myFile->GetSampleRate() / myFile->GetChannelsCount()); + // Perform common initializations + Initialize(); - // Resize the internal buffer so that it can contain 1 second of audio samples - mySamples.resize(myFile->GetSampleRate() * myFile->GetChannelsCount()); + return true; +} - // Initialize the stream - Initialize(myFile->GetChannelsCount(), myFile->GetSampleRate()); + +//////////////////////////////////////////////////////////// +bool Music::OpenFromStream(InputStream& stream) +{ + // First stop the music if it was already running + Stop(); + + // Open the underlying sound file + if (!myFile->OpenRead(stream)) + return false; + + // Perform common initializations + Initialize(); return true; } @@ -127,8 +126,6 @@ bool Music::OnGetData(SoundStream::Chunk& data) } -//////////////////////////////////////////////////////////// -/// /see SoundStream::OnSeek //////////////////////////////////////////////////////////// void Music::OnSeek(Uint32 timeOffset) { @@ -137,4 +134,18 @@ void Music::OnSeek(Uint32 timeOffset) myFile->Seek(timeOffset); } + +//////////////////////////////////////////////////////////// +void Music::Initialize() +{ + // Compute the music duration + myDuration = static_cast(1000 * myFile->GetSamplesCount() / myFile->GetSampleRate() / myFile->GetChannelsCount()); + + // Resize the internal buffer so that it can contain 1 second of audio samples + mySamples.resize(myFile->GetSampleRate() * myFile->GetChannelsCount()); + + // Initialize the stream + SoundStream::Initialize(myFile->GetChannelsCount(), myFile->GetSampleRate()); +} + } // namespace sf diff --git a/src/SFML/Audio/SoundBuffer.cpp b/src/SFML/Audio/SoundBuffer.cpp index 74764afe..d5e09a8d 100644 --- a/src/SFML/Audio/SoundBuffer.cpp +++ b/src/SFML/Audio/SoundBuffer.cpp @@ -80,62 +80,33 @@ SoundBuffer::~SoundBuffer() //////////////////////////////////////////////////////////// bool SoundBuffer::LoadFromFile(const std::string& filename) { - // Open the sound file priv::SoundFile file; if (file.OpenRead(filename)) - { - // Get the sound parameters - std::size_t nbSamples = file.GetSamplesCount(); - unsigned int channelsCount = file.GetChannelsCount(); - unsigned int sampleRate = file.GetSampleRate(); - - // Read the samples from the opened file - mySamples.resize(nbSamples); - if (file.Read(&mySamples[0], nbSamples) == nbSamples) - { - // Update the internal buffer with the new samples - return Update(channelsCount, sampleRate); - } - else - { - return false; - } - } + return Initialize(file); else - { return false; - } } //////////////////////////////////////////////////////////// bool SoundBuffer::LoadFromMemory(const void* data, std::size_t sizeInBytes) { - // Open the sound file priv::SoundFile file; if (file.OpenRead(data, sizeInBytes)) - { - // Get the sound parameters - std::size_t nbSamples = file.GetSamplesCount(); - unsigned int channelsCount = file.GetChannelsCount(); - unsigned int sampleRate = file.GetSampleRate(); - - // Read the samples from the opened file - mySamples.resize(nbSamples); - if (file.Read(&mySamples[0], nbSamples) == nbSamples) - { - // Update the internal buffer with the new samples - return Update(channelsCount, sampleRate); - } - else - { - return false; - } - } + return Initialize(file); + else + return false; +} + + +//////////////////////////////////////////////////////////// +bool SoundBuffer::LoadFromStream(InputStream& stream) +{ + priv::SoundFile file; + if (file.OpenRead(stream)) + return Initialize(file); else - { return false; - } } @@ -153,11 +124,11 @@ bool SoundBuffer::LoadFromSamples(const Int16* samples, std::size_t samplesCount else { // Error... - Err() << "Failed to load sound buffer from memory (" - << "Samples : " << samples << ", " - << "Samples count : " << samplesCount << ", " - << "Channels count : " << channelsCount << ", " - << "Sample rate : " << sampleRate << ")" + Err() << "Failed to load sound buffer from samples (" + << "array: " << samples << ", " + << "count: " << samplesCount << ", " + << "channels: " << channelsCount << ", " + << "samplerate: " << sampleRate << ")" << std::endl; return false; @@ -239,6 +210,28 @@ SoundBuffer& SoundBuffer::operator =(const SoundBuffer& right) } +//////////////////////////////////////////////////////////// +bool SoundBuffer::Initialize(priv::SoundFile& file) +{ + // Retrieve the sound parameters + std::size_t nbSamples = file.GetSamplesCount(); + unsigned int channelsCount = file.GetChannelsCount(); + unsigned int sampleRate = file.GetSampleRate(); + + // Read the samples from the provided file + mySamples.resize(nbSamples); + if (file.Read(&mySamples[0], nbSamples) == nbSamples) + { + // Update the internal buffer with the new samples + return Update(channelsCount, sampleRate); + } + else + { + return false; + } +} + + //////////////////////////////////////////////////////////// bool SoundBuffer::Update(unsigned int channelsCount, unsigned int sampleRate) { @@ -252,7 +245,7 @@ bool SoundBuffer::Update(unsigned int channelsCount, unsigned int sampleRate) // Check if the format is valid if (format == 0) { - Err() << "Unsupported number of channels (" << channelsCount << ")" << std::endl; + Err() << "Failed to load sound buffer (unsupported number of channels: " << channelsCount << ")" << std::endl; return false; } diff --git a/src/SFML/Audio/SoundFile.cpp b/src/SFML/Audio/SoundFile.cpp index 23908ce0..6917045a 100644 --- a/src/SFML/Audio/SoundFile.cpp +++ b/src/SFML/Audio/SoundFile.cpp @@ -26,8 +26,25 @@ // Headers //////////////////////////////////////////////////////////// #include +#include #include #include +#include + + +//////////////////////////////////////////////////////////// +// Private data +//////////////////////////////////////////////////////////// +namespace +{ + // Convert a string to lower case + std::string ToLower(std::string str) + { + for (std::string::iterator i = str.begin(); i != str.end(); ++i) + *i = static_cast(std::tolower(*i)); + return str; + } +} namespace sf @@ -86,7 +103,7 @@ bool SoundFile::OpenRead(const std::string& filename) myFile = sf_open(filename.c_str(), SFM_READ, &fileInfos); if (!myFile) { - Err() << "Failed to read sound file \"" << filename << "\" (" << sf_strerror(myFile) << ")" << std::endl; + Err() << "Failed to open sound file \"" << filename << "\" (" << sf_strerror(myFile) << ")" << std::endl; return false; } @@ -107,14 +124,55 @@ bool SoundFile::OpenRead(const void* data, std::size_t sizeInBytes) sf_close(myFile); // Prepare the memory I/O structure - SF_VIRTUAL_IO io = myMemoryIO.Prepare(data, sizeInBytes); + SF_VIRTUAL_IO io; + io.get_filelen = &Memory::GetLength; + io.read = &Memory::Read; + io.seek = &Memory::Seek; + io.tell = &Memory::Tell; + + // Initialize the memory data + myMemory.DataStart = static_cast(data); + myMemory.DataPtr = myMemory.DataStart; + myMemory.TotalSize = sizeInBytes; // Open the sound file SF_INFO fileInfos; - myFile = sf_open_virtual(&io, SFM_READ, &fileInfos, &myMemoryIO); + myFile = sf_open_virtual(&io, SFM_READ, &fileInfos, &myMemory); if (!myFile) { - Err() << "Failed to read sound file from memory (" << sf_strerror(myFile) << ")" << std::endl; + Err() << "Failed to open sound file from memory (" << sf_strerror(myFile) << ")" << std::endl; + return false; + } + + // Set the sound parameters + myChannelsCount = fileInfos.channels; + mySampleRate = fileInfos.samplerate; + myNbSamples = static_cast(fileInfos.frames) * myChannelsCount; + + return true; +} + + +//////////////////////////////////////////////////////////// +bool SoundFile::OpenRead(InputStream& stream) +{ + // If the file is already opened, first close it + if (myFile) + sf_close(myFile); + + // Prepare the memory I/O structure + SF_VIRTUAL_IO io; + io.get_filelen = &Stream::GetLength; + io.read = &Stream::Read; + io.seek = &Stream::Seek; + io.tell = &Stream::Tell; + + // Open the sound file + SF_INFO fileInfos; + myFile = sf_open_virtual(&io, SFM_READ, &fileInfos, &stream); + if (!myFile) + { + Err() << "Failed to open sound file from stream (" << sf_strerror(myFile) << ")" << std::endl; return false; } @@ -215,120 +273,125 @@ int SoundFile::GetFormatFromFilename(const std::string& filename) ext = filename.substr(pos + 1); // Match every supported extension with its format constant - if (ext == "wav" || ext == "WAV" ) return SF_FORMAT_WAV; - if (ext == "aif" || ext == "AIF" ) return SF_FORMAT_AIFF; - if (ext == "aiff" || ext == "AIFF") return SF_FORMAT_AIFF; - if (ext == "au" || ext == "AU" ) return SF_FORMAT_AU; - if (ext == "raw" || ext == "RAW" ) return SF_FORMAT_RAW; - if (ext == "paf" || ext == "PAF" ) return SF_FORMAT_PAF; - if (ext == "svx" || ext == "SVX" ) return SF_FORMAT_SVX; - if (ext == "nist" || ext == "NIST") return SF_FORMAT_NIST; - if (ext == "voc" || ext == "VOC" ) return SF_FORMAT_VOC; - if (ext == "sf" || ext == "SF" ) return SF_FORMAT_IRCAM; - if (ext == "w64" || ext == "W64" ) return SF_FORMAT_W64; - if (ext == "mat4" || ext == "MAT4") return SF_FORMAT_MAT4; - if (ext == "mat5" || ext == "MAT5") return SF_FORMAT_MAT5; - if (ext == "pvf" || ext == "PVF" ) return SF_FORMAT_PVF; - if (ext == "xi" || ext == "XI" ) return SF_FORMAT_XI; - if (ext == "htk" || ext == "HTK" ) return SF_FORMAT_HTK; - if (ext == "sds" || ext == "SDS" ) return SF_FORMAT_SDS; - if (ext == "avr" || ext == "AVR" ) return SF_FORMAT_AVR; - if (ext == "sd2" || ext == "SD2" ) return SF_FORMAT_SD2; - if (ext == "flac" || ext == "FLAC") return SF_FORMAT_FLAC; - if (ext == "caf" || ext == "CAF" ) return SF_FORMAT_CAF; - if (ext == "wve" || ext == "WVE" ) return SF_FORMAT_WVE; - if (ext == "ogg" || ext == "OGG") return SF_FORMAT_OGG; - if (ext == "mpc2k" || ext == "MPC2K") return SF_FORMAT_MPC2K; - if (ext == "rf64" || ext == "RF64") return SF_FORMAT_RF64; + if (ToLower(ext) == "wav" ) return SF_FORMAT_WAV; + if (ToLower(ext) == "aif" ) return SF_FORMAT_AIFF; + if (ToLower(ext) == "aiff" ) return SF_FORMAT_AIFF; + if (ToLower(ext) == "au" ) return SF_FORMAT_AU; + if (ToLower(ext) == "raw" ) return SF_FORMAT_RAW; + if (ToLower(ext) == "paf" ) return SF_FORMAT_PAF; + if (ToLower(ext) == "svx" ) return SF_FORMAT_SVX; + if (ToLower(ext) == "nist" ) return SF_FORMAT_NIST; + if (ToLower(ext) == "voc" ) return SF_FORMAT_VOC; + if (ToLower(ext) == "sf" ) return SF_FORMAT_IRCAM; + if (ToLower(ext) == "w64" ) return SF_FORMAT_W64; + if (ToLower(ext) == "mat4" ) return SF_FORMAT_MAT4; + if (ToLower(ext) == "mat5" ) return SF_FORMAT_MAT5; + if (ToLower(ext) == "pvf" ) return SF_FORMAT_PVF; + if (ToLower(ext) == "xi" ) return SF_FORMAT_XI; + if (ToLower(ext) == "htk" ) return SF_FORMAT_HTK; + if (ToLower(ext) == "sds" ) return SF_FORMAT_SDS; + if (ToLower(ext) == "avr" ) return SF_FORMAT_AVR; + if (ToLower(ext) == "sd2" ) return SF_FORMAT_SD2; + if (ToLower(ext) == "flac" ) return SF_FORMAT_FLAC; + if (ToLower(ext) == "caf" ) return SF_FORMAT_CAF; + if (ToLower(ext) == "wve" ) return SF_FORMAT_WVE; + if (ToLower(ext) == "ogg" ) return SF_FORMAT_OGG; + if (ToLower(ext) == "mpc2k") return SF_FORMAT_MPC2K; + if (ToLower(ext) == "rf64" ) return SF_FORMAT_RF64; return -1; } //////////////////////////////////////////////////////////// -SF_VIRTUAL_IO SoundFile::MemoryIO::Prepare(const void* data, std::size_t sizeInBytes) +sf_count_t SoundFile::Memory::GetLength(void* user) { - // Setup the I/O functions - SF_VIRTUAL_IO io; - io.get_filelen = &SoundFile::MemoryIO::GetLength; - io.read = &SoundFile::MemoryIO::Read; - io.seek = &SoundFile::MemoryIO::Seek; - io.tell = &SoundFile::MemoryIO::Tell; - io.write = &SoundFile::MemoryIO::Write; - - // Initialize the memory data - myDataStart = static_cast(data); - myDataPtr = myDataStart; - myTotalSize = sizeInBytes; - - return io; + Memory* memory = static_cast(user); + return memory->TotalSize; } //////////////////////////////////////////////////////////// -sf_count_t SoundFile::MemoryIO::GetLength(void* userData) +sf_count_t SoundFile::Memory::Read(void* ptr, sf_count_t count, void* user) { - MemoryIO* self = static_cast(userData); + Memory* memory = static_cast(user); - return self->myTotalSize; -} - - -//////////////////////////////////////////////////////////// -sf_count_t SoundFile::MemoryIO::Read(void* ptr, sf_count_t count, void* userData) -{ - MemoryIO* self = static_cast(userData); - - sf_count_t position = self->myDataPtr - self->myDataStart; - if (position + count >= self->myTotalSize) - count = self->myTotalSize - position; - - std::memcpy(ptr, self->myDataPtr, static_cast(count)); - - self->myDataPtr += count; + sf_count_t position = memory->DataPtr - memory->DataStart; + if (position + count >= memory->TotalSize) + count = memory->TotalSize - position; + std::memcpy(ptr, memory->DataPtr, static_cast(count)); + memory->DataPtr += count; return count; } //////////////////////////////////////////////////////////// -sf_count_t SoundFile::MemoryIO::Seek(sf_count_t offset, int whence, void* userData) +sf_count_t SoundFile::Memory::Seek(sf_count_t offset, int whence, void* user) { - MemoryIO* self = static_cast(userData); - + Memory* memory = static_cast(user); sf_count_t position = 0; switch (whence) { case SEEK_SET : position = offset; break; - case SEEK_CUR : position = self->myDataPtr - self->myDataStart + offset; break; - case SEEK_END : position = self->myTotalSize - offset; break; + case SEEK_CUR : position = memory->DataPtr - memory->DataStart + offset; break; + case SEEK_END : position = memory->TotalSize - offset; break; default : position = 0; break; } - if (position >= self->myTotalSize) - position = self->myTotalSize - 1; + if (position >= memory->TotalSize) + position = memory->TotalSize - 1; else if (position < 0) position = 0; - self->myDataPtr = self->myDataStart + position; - + memory->DataPtr = memory->DataStart + position; return position; } //////////////////////////////////////////////////////////// -sf_count_t SoundFile::MemoryIO::Tell(void* userData) +sf_count_t SoundFile::Memory::Tell(void* user) { - MemoryIO* self = static_cast(userData); - - return self->myDataPtr - self->myDataStart; + Memory* memory = static_cast(user); + return memory->DataPtr - memory->DataStart; } //////////////////////////////////////////////////////////// -sf_count_t SoundFile::MemoryIO::Write(const void*, sf_count_t, void*) +sf_count_t SoundFile::Stream::GetLength(void* userData) { - return 0; + sf::InputStream* stream = static_cast(userData); + return stream->GetSize(); +} + + +//////////////////////////////////////////////////////////// +sf_count_t SoundFile::Stream::Read(void* ptr, sf_count_t count, void* userData) +{ + sf::InputStream* stream = static_cast(userData); + return stream->Read(reinterpret_cast(ptr), count); +} + + +//////////////////////////////////////////////////////////// +sf_count_t SoundFile::Stream::Seek(sf_count_t offset, int whence, void* userData) +{ + sf::InputStream* stream = static_cast(userData); + switch (whence) + { + case SEEK_SET : return stream->Seek(offset); + case SEEK_CUR : return stream->Seek(stream->GetPosition() + offset); + case SEEK_END : return stream->Seek(stream->GetSize() - offset); + default : return stream->Seek(0); + } +} + + +//////////////////////////////////////////////////////////// +sf_count_t SoundFile::Stream::Tell(void* userData) +{ + sf::InputStream* stream = static_cast(userData); + return stream->GetPosition(); } } // namespace priv diff --git a/src/SFML/Audio/SoundFile.hpp b/src/SFML/Audio/SoundFile.hpp index 0451a345..4695df05 100644 --- a/src/SFML/Audio/SoundFile.hpp +++ b/src/SFML/Audio/SoundFile.hpp @@ -35,6 +35,8 @@ namespace sf { +class InputStream; + namespace priv { //////////////////////////////////////////////////////////// @@ -102,6 +104,16 @@ public : //////////////////////////////////////////////////////////// bool OpenRead(const void* data, std::size_t sizeInBytes); + //////////////////////////////////////////////////////////// + /// \brief Open a sound file from a custom stream for reading + /// + /// \param stream Source stream to read from + /// + /// \return True if the file was successfully opened + /// + //////////////////////////////////////////////////////////// + bool OpenRead(InputStream& stream); + //////////////////////////////////////////////////////////// /// \brief a the sound file for writing /// @@ -156,33 +168,38 @@ private : static int GetFormatFromFilename(const std::string& filename); //////////////////////////////////////////////////////////// - /// \brief Provide I/O functions for manipulating files in memory + /// \brief Data and callbacks for opening from memory /// //////////////////////////////////////////////////////////// - class MemoryIO + struct Memory { - public : + const char* DataStart; + const char* DataPtr; + sf_count_t TotalSize; - SF_VIRTUAL_IO Prepare(const void* data, std::size_t sizeInBytes); + static sf_count_t GetLength(void* user); + static sf_count_t Read(void* ptr, sf_count_t count, void* user); + static sf_count_t Seek(sf_count_t offset, int whence, void* user); + static sf_count_t Tell(void* user); + }; - private : - - static sf_count_t GetLength(void* userData); - static sf_count_t Read(void* ptr, sf_count_t count, void* userData); - static sf_count_t Seek(sf_count_t offset, int whence, void* userData); - static sf_count_t Tell(void* userData); - static sf_count_t Write(const void* ptr, sf_count_t count, void* userData); - - const char* myDataStart; - const char* myDataPtr; - sf_count_t myTotalSize; + //////////////////////////////////////////////////////////// + /// \brief Callbacks for opening from stream + /// + //////////////////////////////////////////////////////////// + struct Stream + { + static sf_count_t GetLength(void* user); + static sf_count_t Read(void* ptr, sf_count_t count, void* user); + static sf_count_t Seek(sf_count_t offset, int whence, void* user); + static sf_count_t Tell(void* user); }; //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// SNDFILE* myFile; ///< File descriptor - MemoryIO myMemoryIO; ///< Memory read / write data + Memory myMemory; ///< Memory reading info std::size_t myNbSamples; ///< Total number of samples in the file unsigned int myChannelsCount; ///< Number of channels used by the sound unsigned int mySampleRate; ///< Number of samples per second diff --git a/src/SFML/Graphics/Font.cpp b/src/SFML/Graphics/Font.cpp index 2237ff28..e8e5ced1 100644 --- a/src/SFML/Graphics/Font.cpp +++ b/src/SFML/Graphics/Font.cpp @@ -26,21 +26,43 @@ // Headers //////////////////////////////////////////////////////////// #include +#include #include #include #include FT_FREETYPE_H #include FT_GLYPH_H #include FT_OUTLINE_H #include FT_BITMAP_H +#include + + +//////////////////////////////////////////////////////////// +// Private data +//////////////////////////////////////////////////////////// +namespace +{ + // FreeType callbacks that operate on a sf::InputStream + unsigned long Read(FT_Stream rec, unsigned long offset, unsigned char* buffer, unsigned long count) + { + sf::InputStream* stream = static_cast(rec->descriptor.pointer); + if (stream->Seek(offset) != offset) + return count ? 1 : 0; // error code is 0 if we're reading, or nonzero if we're seeking + return static_cast(stream->Read(reinterpret_cast(buffer), count)); + } + void Close(FT_Stream) + { + } +} namespace sf { //////////////////////////////////////////////////////////// Font::Font() : -myLibrary (NULL), -myFace (NULL), -myRefCount(NULL) +myLibrary (NULL), +myFace (NULL), +myStreamRec(NULL), +myRefCount (NULL) { } @@ -51,6 +73,7 @@ Font::Font(const Font& copy) : Resource(), myLibrary (copy.myLibrary), myFace (copy.myFace), +myStreamRec (copy.myStreamRec), myRefCount (copy.myRefCount), myPages (copy.myPages), myPixelBuffer(copy.myPixelBuffer) @@ -150,6 +173,63 @@ bool Font::LoadFromMemory(const void* data, std::size_t sizeInBytes) } +//////////////////////////////////////////////////////////// +bool Font::LoadFromStream(InputStream& stream) +{ + // Cleanup the previous resources + Cleanup(); + myRefCount = new int(1); + + // Initialize FreeType + // Note: we initialize FreeType for every font instance in order to avoid having a single + // global manager that would create a lot of issues regarding creation and destruction order. + FT_Library library; + if (FT_Init_FreeType(&library) != 0) + { + Err() << "Failed to load font from stream (failed to initialize FreeType)" << std::endl; + return false; + } + myLibrary = library; + + // Prepare a wrapper for our stream, that we'll pass to FreeType callbacks + FT_StreamRec* rec = new FT_StreamRec; + std::memset(rec, 0, sizeof(rec)); + rec->base = NULL; + rec->size = static_cast(stream.GetSize()); + rec->pos = 0; + rec->descriptor.pointer = &stream; + rec->read = &Read; + rec->close = &Close; + + // Setup the FreeType callbacks that will read our stream + FT_Open_Args args; + args.flags = FT_OPEN_STREAM; + args.stream = rec; + args.driver = 0; + + // Load the new font face from the specified stream + FT_Face face; + if (FT_Open_Face(static_cast(myLibrary), &args, 0, &face) != 0) + { + Err() << "Failed to load font from stream (failed to create the font face)" << std::endl; + 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 false; + } + + // Store the loaded font in our ugly void* :) + myFace = face; + myStreamRec = rec; + + return true; +} + + //////////////////////////////////////////////////////////// const Glyph& Font::GetGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) const { @@ -284,6 +364,10 @@ void Font::Cleanup() if (myFace) FT_Done_Face(static_cast(myFace)); + // Destroy the stream rec instance, if any (must be done after FT_Done_Face!) + if (myStreamRec) + delete static_cast(myStreamRec); + // Close the library if (myLibrary) FT_Done_FreeType(static_cast(myLibrary)); @@ -291,9 +375,10 @@ void Font::Cleanup() } // Reset members - myLibrary = NULL; - myFace = NULL; - myRefCount = NULL; + myLibrary = NULL; + myFace = NULL; + myStreamRec = NULL; + myRefCount = NULL; myPages.clear(); myPixelBuffer.clear(); } diff --git a/src/SFML/Graphics/Image.cpp b/src/SFML/Graphics/Image.cpp index bc5aa043..af7b8a24 100644 --- a/src/SFML/Graphics/Image.cpp +++ b/src/SFML/Graphics/Image.cpp @@ -136,6 +136,28 @@ bool Image::LoadFromMemory(const void* data, std::size_t size) } +//////////////////////////////////////////////////////////// +bool Image::LoadFromStream(InputStream& stream) +{ + // Forward the job to the image loader + std::vector pixels; + unsigned int width; + unsigned int height; + if (priv::ImageLoader::GetInstance().LoadImageFromStream(stream, pixels, width, height)) + { + // Loading succeeded : we can create the texture + if (CreateTexture(width, height)) + { + // Copy the pixels + myPixels.swap(pixels); + return true; + } + } + + return false; +} + + //////////////////////////////////////////////////////////// bool Image::LoadFromPixels(unsigned int width, unsigned int height, const Uint8* data) { diff --git a/src/SFML/Graphics/ImageLoader.cpp b/src/SFML/Graphics/ImageLoader.cpp index a154b2df..f7f4775c 100644 --- a/src/SFML/Graphics/ImageLoader.cpp +++ b/src/SFML/Graphics/ImageLoader.cpp @@ -26,6 +26,7 @@ // Headers //////////////////////////////////////////////////////////// #include +#include #include #include #define STB_IMAGE_WRITE_IMPLEMENTATION @@ -38,6 +39,9 @@ extern "C" #include +//////////////////////////////////////////////////////////// +// Private data +//////////////////////////////////////////////////////////// namespace { // Convert a string to lower case @@ -47,6 +51,23 @@ namespace *i = static_cast(std::tolower(*i)); return str; } + + // stb_image callbacks that operate on a sf::InputStream + int Read(void* user, char* data, int size) + { + sf::InputStream* stream = static_cast(user); + return static_cast(stream->Read(data, size)); + } + void Skip(void* user, unsigned int size) + { + sf::InputStream* stream = static_cast(user); + stream->Seek(stream->GetPosition() + size); + } + int Eof(void* user) + { + sf::InputStream* stream = static_cast(user); + return stream->GetPosition() >= stream->GetSize(); + } } @@ -157,6 +178,47 @@ bool ImageLoader::LoadImageFromMemory(const void* data, std::size_t size, std::v } +//////////////////////////////////////////////////////////// +bool ImageLoader::LoadImageFromStream(InputStream& stream, std::vector& pixels, unsigned int& width, unsigned int& height) +{ + // Clear the array (just in case) + pixels.clear(); + + // Setup the stb_image callbacks + stbi_io_callbacks callbacks; + callbacks.read = &Read; + callbacks.skip = &Skip; + callbacks.eof = &Eof; + + // Load the image and get a pointer to the pixels in memory + int imgWidth, imgHeight, imgChannels; + unsigned char* ptr = stbi_load_from_callbacks(&callbacks, &stream, &imgWidth, &imgHeight, &imgChannels, STBI_rgb_alpha); + + if (ptr && imgWidth && imgHeight) + { + // Assign the image properties + width = imgWidth; + height = imgHeight; + + // Copy the loaded pixels to the pixel buffer + pixels.resize(width * height * 4); + memcpy(&pixels[0], ptr, pixels.size()); + + // Free the loaded pixels (they are now in our own pixel buffer) + stbi_image_free(ptr); + + return true; + } + else + { + // Error, failed to load the image + Err() << "Failed to load image from stream. Reason : " << stbi_failure_reason() << std::endl; + + return false; + } +} + + //////////////////////////////////////////////////////////// bool ImageLoader::SaveImageToFile(const std::string& filename, const std::vector& pixels, unsigned int width, unsigned int height) { diff --git a/src/SFML/Graphics/ImageLoader.hpp b/src/SFML/Graphics/ImageLoader.hpp index 581437ef..8cc97238 100644 --- a/src/SFML/Graphics/ImageLoader.hpp +++ b/src/SFML/Graphics/ImageLoader.hpp @@ -35,6 +35,8 @@ namespace sf { +class InputStream; + namespace priv { //////////////////////////////////////////////////////////// @@ -67,7 +69,7 @@ public : bool LoadImageFromFile(const std::string& filename, std::vector& pixels, unsigned int& width, unsigned int& height); //////////////////////////////////////////////////////////// - /// \brief Load an image from a file inn memory + /// \brief Load an image from a file in memory /// /// \param data Pointer to the file data in memory /// \param size Size of the data to load, in bytes @@ -80,6 +82,19 @@ public : //////////////////////////////////////////////////////////// bool LoadImageFromMemory(const void* data, std::size_t size, std::vector& pixels, unsigned int& width, unsigned int& height); + //////////////////////////////////////////////////////////// + /// \brief Load an image from a custom stream + /// + /// \param stream Source stream to read from + /// \param pixels Array of pixels to fill with loaded image + /// \param width Width of loaded image, in pixels + /// \param height Height of loaded image, in pixels + /// + /// \return True if loading was successful + /// + //////////////////////////////////////////////////////////// + bool LoadImageFromStream(InputStream& stream, std::vector& pixels, unsigned int& width, unsigned int& height); + //////////////////////////////////////////////////////////// /// \bref Save an array of pixels as an image file /// diff --git a/src/SFML/Graphics/Shader.cpp b/src/SFML/Graphics/Shader.cpp index c078e9f3..798decde 100644 --- a/src/SFML/Graphics/Shader.cpp +++ b/src/SFML/Graphics/Shader.cpp @@ -28,9 +28,10 @@ //////////////////////////////////////////////////////////// #include #include +#include #include #include -#include +#include namespace sf @@ -100,6 +101,19 @@ bool Shader::LoadFromMemory(const std::string& shader) } +//////////////////////////////////////////////////////////// +bool Shader::LoadFromStream(InputStream& stream) +{ + // Read the shader code from the stream + std::vector buffer(static_cast(stream.GetSize())); + Int64 read = stream.Read(&buffer[0], buffer.size()); + myFragmentShader.assign(&buffer[0], &buffer[0] + read); + + // Create the shaders and the program + return CompileProgram(); +} + + //////////////////////////////////////////////////////////// void Shader::SetParameter(const std::string& name, float x) {