#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2021 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License #ifndef NUCLEX_PIXELS_STORAGE_BITMAPSERIALIZER_H #define NUCLEX_PIXELS_STORAGE_BITMAPSERIALIZER_H #include "Nuclex/Pixels/Config.h" #include "Nuclex/Pixels/Bitmap.h" #include "Nuclex/Pixels/BitmapInfo.h" #include // for std::atomic #include // for std::string #include // for std::vector #include // for std::unordered_map #include // for std::unique_ptr #include // for std::optional namespace Nuclex { namespace Pixels { namespace Storage { // ------------------------------------------------------------------------------------------- // class BitmapCodec; class VirtualFile; // ------------------------------------------------------------------------------------------- // /// Allows reading and writing Bitmaps from/to a stream of bytes /// /// /// This class enables you to turn Bitmaps into byte streams using common file /// format such as .png, .jpg or any other file format the serializer is given /// a "codec" for. /// /// /// For simple file operations, the serializer has overloads that access plain /// files from a path, turning the loading and saving of common image formats /// into one-liners. For advanced usage, you can use your own VirtualFile /// implementation to read data from any data source you like. /// /// /// The bitmap serializer will select the correct codec either by file extension, /// or, if no file extension was provided, by letting each registered codec /// check the file header. The order in which codecs are tested tries the most /// recently used codecs first (assuming that if your game or application commonly /// uses only one or two file formats) /// /// /// /// int main() { /// Nuclex::Pixels::Storage::BitmapSerializer serializer; /// /// Bitmap splashScreen = serializer.Load(u8"splash-screen.png"); /// // Do something with the splash screen bitmap /// /// return 0; /// } /// /// /// /// You can use the BitmapSerializer to load and save images on multiple threads. /// The only requirement is that you must not use the /// method simultaneously. A good design would be to set up your BitmapSerializer /// instance once, register your custom codecs up front, call /// std::atomic_thread_fence() for good measure and then let your /// threads access the BitmapSerialier as a const reference so they /// can only access the and methods. /// /// class NUCLEX_PIXELS_TYPE BitmapSerializer { /// Initializes a new bitmap serializer public: NUCLEX_PIXELS_API BitmapSerializer(); /// Frees all resources owned by a bitmap serializer public: NUCLEX_PIXELS_API ~BitmapSerializer(); /// Registers a bitmap codec to load and/or save a file format /// Bitmap codec that will be registered public: NUCLEX_PIXELS_API void RegisterCodec(std::unique_ptr &&codec); /// Registers a bitmap codec to load and/or save a file format /// Type of bitmap codec that will be registered public: template void RegisterCodec() { std::unique_ptr codec = std::make_unique(); RegisterCodec(std::move(codec)); } #if 0 // This method can be written, but is it needed? /// Registers a bitmap codec to load and/or save a file format /// Type of bitmap codec that will be registered public: template void UnregisterCodec() { UnregisterCodec(typeid(TCodec)); } #endif /// Tries to read informations about a bitmap /// File from which informations will be read /// Optional file extension the loaded data had /// Informations about the bitmap, if it is a supported format public: NUCLEX_PIXELS_API std::optional TryReadInfo( const VirtualFile &file, const std::string &extensionHint = std::string() ) const; /// Tries to read informations about a bitmap /// Path of the file informations will be read from /// Informations about the bitmap, if it is a supported format public: NUCLEX_PIXELS_API std::optional TryReadInfo(const std::string &path) const; /// Checks whether the bitmap store can load the specified file /// File the bitmap store will check /// /// Optional file extension to help detection (may speed things up) /// /// True if the bitmap store thinks it can load the file public: NUCLEX_PIXELS_API bool CanLoad( const VirtualFile &file, const std::string &extensionHint = std::string() ) const; /// Checks whether the bitmap store can load the specified file /// Path of the file the bitmap store will check /// True if the bitmap store thinks it can load the file public: NUCLEX_PIXELS_API bool CanLoad(const std::string &path) const; /// Loads the specified file into a new Bitmap /// File the bitmap store will load /// /// Optional file extension to help detection (may speed things up) /// /// The bitmap loaded from the specified file public: NUCLEX_PIXELS_API Bitmap Load( const VirtualFile &file, const std::string &extensionHint = std::string() ) const; /// Loads the specified file into a new Bitmap /// Path of the file the bitmap store will load /// The bitmap loaded from the specified file public: NUCLEX_PIXELS_API Bitmap Load(const std::string &path) const; /// Loads the specified file into a new Bitmap /// File the bitmap store will load /// /// Optional file extension to help detection (may speed things up) /// /// The bitmap loaded from the specified file /// /// This method is useful if you have an application that can, for example, evict /// textures from video memory (or use a graphics API where the OS can release textures /// during task switching). Instead of keeping a system memory copy of each texture, /// a bitmap can be constructed to access the texture memory directly and this method /// can then restore the texture to its former glory). /// copy, you can /// public: NUCLEX_PIXELS_API void Reload( Bitmap &exactFittingBitmap, const VirtualFile &file, const std::string &extensionHint = std::string() ) const; /// Loads the specified file into a new Bitmap /// Path of the file the bitmap store will load /// The bitmap loaded from the specified file /// /// This method is useful if you have an application that can, for example, evict textures /// from video memory (or use a graphics API where the OS can release textures during /// task switching). Instead of keeping a system memory copy of each texture, a bitmap /// can be constructed to access the texture memory directly and this method can then /// restore the texture to its former glory). /// copy, you can /// public: NUCLEX_PIXELS_API void Reload( Bitmap &exactFittingBitmap, const std::string &path ) const; /// Saves a bitmap into the specified file /// Bitmap that will be saved into a file /// File the bitmap will be saved into /// File extension used to select the file format /// /// How much effort (CPU time) should be put into reducing the size of the image. /// Image formats may ignore this parameter completely. If possible, setting it /// to 0.0 will write an uncompressed image file. /// /// /// How much image quality should be prioritized over achieving small file sizes. /// This parameter will only be used by lossy image formats supporting such /// settings in their compression algorithms. /// public: NUCLEX_PIXELS_API void Save( const Bitmap &bitmap, VirtualFile &file, const std::string &extension, float compressionEffortHint = 0.75f, float outputQualityHint = 0.95f ) const; /// Saves a bitmap into the specified file /// Bitmap that will be saved into a file /// Path under which the file will be saved /// /// File extension used to select the file format. Leave empty to try and obtain /// the extension from the specified target path. /// /// /// How much effort (CPU time) should be put into reducing the size of the image. /// Image formats may ignore this parameter completely. If possible, setting it /// to 0.0 will write an uncompressed image file. /// /// /// How much image quality should be prioritized over achieving small file sizes. /// This parameter will only be used by lossy image formats supporting such /// settings in their compression algorithms. /// public: NUCLEX_PIXELS_API void Save( const Bitmap &bitmap, const std::string &path, const std::string &extension = std::string(), float compressionEffortHint = 0.75f, float outputQualityHint = 0.95f ) const; /// Looks up the codec for a file format from the file extension /// File extension for which the codec will be looked up /// A reference to the bitmap codec associated with the extension /// /// This will throw the appropriate exception if no matching codec is found and also /// if a codec is found but it does not support saving images. /// private: const BitmapCodec &getSavingCodecForExtension(const std::string &extension) const; /// Builds a new iterator that checks codecs in the most likely order /// File extension, if known /// /// Address of a method that will be called to try each registered codec /// /// True as soon as one codec reports success, false if none did /// /// This is only a template so I don't have do expose the iterator implementation /// in a public header. There's exactly one specialization of the method. /// private: template bool tryCodecsInOptimalOrder( const std::string &extension, bool (*tryCodecCallback)( const BitmapCodec &codec, const std::string &extension, TOutput &result ), TOutput &result ) const; /// Updates the list of most recently used codecs /// Index of the codec that was most recently used private: void updateMostRecentCodecIndex(std::size_t codecIndex) const; /// Maps file extensions to codec indices private: typedef std::unordered_map ExtensionCodecIndexMap; /// Stores a sequential list of codecs private: typedef std::vector> CodecVector; /// Allows the bitmap store to look up a codec by its file extension /// /// Extensions are stored in UTF-8 folded lowercase for case insensitivity. /// private: ExtensionCodecIndexMap codecsByExtension; /// Codecs that have been registered with the bitmap store private: CodecVector codecs; /// Codec that was most recently accessed, -1 if none private: mutable std::atomic mostRecentCodecIndex; /// Codec that was second-most recently accessed, -1 if none private: mutable std::atomic secondMostRecentCodecIndex; }; // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Pixels::Storage #endif // NUCLEX_PIXELS_STORAGE_BITMAPSERIALIZER_H