/** * @file Image.hpp * * Purpose Class declaration of Image class * * Authors Dinesh Iyer * * Copyright 2019-2021 MathWorks, Inc. * */ #ifndef imagesdatatypesimage_header #define imagesdatatypesimage_header #include "datatypes_util.hpp" #include "ImageDefs.hpp" #include "ImageMetadata.hpp" #include namespace images { namespace datatypes { class IMAGESDATATYPESIMAGE_API Image { private: // ------------------------------------------------ // Typedef's used // ------------------------------------------------ typedef std::default_delete RawDataDeleter; // ------------------------------------------------ // Interface of class that manages the lifetime of // the underlying data // ------------------------------------------------ class IDataUnderlyingMgr { public: virtual ~IDataUnderlyingMgr() noexcept {} // Gain access to the raw data buffer virtual BufferType* getDataUnderlying() const = 0; // Give-up ownership of the underlying data buffer virtual BufferType* releaseDataUnderlying() = 0; }; // ------------------------------------------------ // Takes ownership of the underling data. // Memory for underlying data is de-allocated if no // references to it remain. // ------------------------------------------------ class DataUnderlyingMgrOwn : public IDataUnderlyingMgr { public: DataUnderlyingMgrOwn(BufferType* src) :fDataUnderlying(nullptr) { if( src == nullptr ) { return; } fDataUnderlying.reset(src, RawDataDeleter()); } ~DataUnderlyingMgrOwn() noexcept {} BufferType* getDataUnderlying() const override { return fDataUnderlying.get(); } BufferType* releaseDataUnderlying() override { // @TODO return nullptr; } private: std::shared_ptr fDataUnderlying; }; // ------------------------------------------------ // Uses the pointer provided as the underlying data // Does not dellocate memory. // ------------------------------------------------ class DataUnderlyingMgrUseOnly : public IDataUnderlyingMgr { public: DataUnderlyingMgrUseOnly(BufferType* src) :fDataUnderlying(src) {} ~DataUnderlyingMgrUseOnly() noexcept {} BufferType* getDataUnderlying() const override { return fDataUnderlying; } BufferType* releaseDataUnderlying() override { auto tempBuffer = fDataUnderlying; fDataUnderlying = nullptr; return tempBuffer; } private: BufferType* fDataUnderlying; }; // ------------------------------------------------ // Takes ownership of the underling data. // Supports relinquishing the underlying data without // doing a deep copy of the data // ------------------------------------------------ class DataUnderlyingMgrSingleUse : public IDataUnderlyingMgr { public: DataUnderlyingMgrSingleUse(BufferType* src) : fDataUnderlying( src, RawDataDeleter() ) {} ~DataUnderlyingMgrSingleUse() noexcept {} BufferType* getDataUnderlying() const override { return fDataUnderlying.get(); } BufferType* releaseDataUnderlying() override { return fDataUnderlying.release(); } private: std::unique_ptr fDataUnderlying; }; public: // ------------------------------------------------ // Life time // ------------------------------------------------ /** * Default constructor * This represent an empty image * Also required by MATLAB Coder */ Image(); /** * Construct an image. * * Allow users to create an image with certain optimizations that * can avoid unnecessary copies of the underlying data. * * The image created with RelinquishDataUnderlying::YES can be copied and assigned. * However, when copied or assigned, the target image will contain a deep copy * of the data of the source image. For IM1 created with this flag = TRUE * Image im2 = im1; % IM2 will have its own copy of the data buffer i.e * deep copy will be done * * @param - rows - Number of rows in the image * @param - columns - Number of columns in the image * @param - numChannels - Number of channels in the image * @param - dtype - Datatype of the underlying data * @param - format - ColorFormat of the image data * @param - layout - Layout of the image data * @param - relinquish - YES indicates the instance can give up its * underlying data buffer without a data copy */ Image( DimType rows, DimType columns, DimType numChannels, ClassUnderlying dtype, ColorFormat format, Layout layout, RelinquishDataUnderlying relinquish = RelinquishDataUnderlying::NO ); /** * Construct an image. * * Allow users to create an image with certain optimizations that * can avoid unnecessary copies of the underlying data. * * The image created with RelinquishDataUnderlying::YES can be copied and assigned. * However, when copied or assigned, the target image will contain a deep copy * of the data of the source image. For IM1 created with this flag = TRUE * Image im2 = im1; % IM2 will have its own copy of the data buffer i.e * deep copy will be done * * @param - metadata - Instance of ImageMetadata class * @param - relinquish - YES indicates the instance can give up its * underlying data buffer without a data copy */ Image( const ImageMetadata& metadata, RelinquishDataUnderlying relinquish = RelinquishDataUnderlying::NO ); /** * Construct an image. * * Allow users to create an image by specifying the dimensions, datatype, * memory arrangement and also provide the buffer containing the image * data * * * @param - src - Source buffer containing data. Assumed that src has * sufficient number of bytes to store the entire image * data * @param - rows - Number of rows in the image * @param - columns - Number of columns in the image * @param - numChannels - Number of channels in the image * @param - dtype - Datatype of the underlying data * @param - format - ColorFormat of the image data * @param - layout - Layout of the image data * @param - mgmt - Determine how the input source of data needs to be * handled by the image. By default, a deep copy of the * source is made. * @param - relinquish - YES indicates the instance can give up its * underlying data buffer without a data copy */ Image( void* src, DimType rows, DimType columns, DimType numChannels, ClassUnderlying dtype, ColorFormat format, Layout layout, SourceMgmt mgmt = SourceMgmt::COPY_SRC, RelinquishDataUnderlying relinquish = RelinquishDataUnderlying::NO ); /** * Construct an image. * * Allow users to create an image by specifying the dimensions, datatype, * memory arrangement and also provide the buffer containing the image * data * * * @param - src - Source buffer containing data. Assumed that src has * sufficient number of bytes to store the entire image * data * @param - rows - Number of rows in the image * @param - columns - Number of columns in the image * @param - numChannels - Number of channels in the image * @param - dtype - Datatype of the underlying data * @param - format - ColorFormat of the image data * @param - layout - Layout of the image data */ Image( void const* dataUnderlying, DimType rows, DimType columns, DimType numChannels, ClassUnderlying dtype, ColorFormat format, Layout layout ); /** * Construct an image. * * Allow users to create an image by specifying the dimensions, datatype, * memory arrangement and also provide the buffer containing the image * data * * * @param - src - Source buffer containing data. Assumed that src has * sufficient number of bytes to store the entire image * data * @param - metadata - Instance of ImageMetadata class * @param - mgmt - Determine how the input source of data needs to be * handled by the image. By default, a deep copy of the * source is made. * @param - relinquish - YES indicates the instance can give up its * underlying data buffer without a data copy */ Image( void* src, const ImageMetadata& metadata, SourceMgmt mgmt = SourceMgmt::COPY_SRC, RelinquishDataUnderlying relinquish = RelinquishDataUnderlying::NO ); // Not really necessary. But adding for completeness ~Image() noexcept; // ------------------------------------------------ // Copy and assignment operations // ------------------------------------------------ /** * Perform a shallow copy of the image */ Image(const Image& otherImage); Image& operator=(const Image& otherImage); // Move operations Image(Image&& otherImage) noexcept; Image& operator=(Image&& otherImage) noexcept; // ------------------------------------------------ // Public methods // ------------------------------------------------ /** * Creates a deep copy i.e. allocates a new memory buffer * and copies data into it. * * @param - relinquish - YES indicates the instance can give up its * underlying data buffer without a data copy * @return - Image */ Image deepCopy(RelinquishDataUnderlying relinquish = RelinquishDataUnderlying::NO) const; /** * Access the underlying data buffer * * Using the data buffer, the user can modify the contents of the data. * As the internal buffer is a shared resource, changing it can have * indeterminate consequences. * So, if the user wants to modify the buffer, the recommendation is * to create a deepCopy() FIRST. * * @param - none * @return - Pointer of type BufferType */ BufferType* getDataUnderlying() const; BufferType* getUniqueDataUnderlying(); /** * Query whether calling releaseDataUnderlying() makes a deep copy * of the underlying data before passing it to the user * * This is necessary if the user wants to track when a memcpy is being * done when working with Image instances. The table specified in this * spec contains details. * * @param - none * @return - bool. TRUE if a deep copy will be done when calling releaseDataUnderlying */ bool isDeepCopyOfDataOnRelease() const; /** * Give up ownership of the underlying data buffer * * This is useful if the user wants to pass the data to another * object that can take ownership of data buffers. * NOTE: Using the Image instance to access image data after calling * this method have indeterminate behaviour. * * @param - none * @return - Pointer of type BufferType */ BufferType* releaseDataUnderlying(); /** * Resize the image * * This method "blows out" any valid image data that is held by the * image instance and allocates a new buffer that has sufficient * space to hold image data for the newer dimensions. * This method allows changing only the number of rows and/or columns * AND not the number of channels or any other properties. * * @param - newNumRows - Number of rows in the target image * @param - newNumCols - Number of columns in the target image * @return - none */ void resize(DimType newNumRows, DimType newNumCols); size_t computeNumBytesInImage() const; // ------------------------------------------------ // Property Getters/Setters // ------------------------------------------------ DimType getRows() const { return fMetadata.getRows(); } DimType getColumns() const { return fMetadata.getColumns(); } DimType getNumChannels() const { return fMetadata.getNumChannels(); } ClassUnderlying getClassUnderlying() const { return fMetadata.getClassUnderlying(); } ColorFormat getColorFormat() const { return fMetadata.getColorFormat(); } Layout getLayout() const { return fMetadata.getLayout(); } private: // ------------------------------------------------ // Private Helper functions // ------------------------------------------------ void copyProps(const Image& otherImage); BufferType* allocateBufferForDataUnderlying() const; static IDataUnderlyingMgr* createDataUnderlyingMgr( BufferType* src, SourceMgmt mgmt = SourceMgmt::COPY_SRC, RelinquishDataUnderlying relinquish = RelinquishDataUnderlying::NO ); std::unique_ptr copyRawData(const BufferType* src) const; // ------------------------------------------------ // Properties // ------------------------------------------------ ImageMetadata fMetadata; // Instance of class that manages the raw image data std::shared_ptr fDataUnderlyingMgr; // Tracks whether the instance can relinquish underlying data // without performing a deep copy. RelinquishDataUnderlying fRelinquish; }; inline Image makeImage(void const* dataUnderlying, DimType rows, DimType columns, DimType numChannels, ClassUnderlying classUnderlying, ColorFormat colorFormat, Layout layout) { return Image( dataUnderlying, rows, columns, numChannels, classUnderlying, colorFormat, layout); } inline Image makeImage(DimType rows, DimType columns, DimType numChannels, ClassUnderlying classUnderlying, ColorFormat colorFormat, Layout layout) { return Image( rows, columns, numChannels, classUnderlying, colorFormat, layout); } } } #endif // imagesDatatypesimage_header