//*****************************************************************************
// Copyright 2018 NVIDIA Corporation. All rights reserved.
//*****************************************************************************
/// \file
/// \brief API component for various image-related functions.
//*****************************************************************************

#ifndef MI_NEURAYLIB_IIMAGE_API_H
#define MI_NEURAYLIB_IIMAGE_API_H

#include <mi/base/interface_declare.h>

namespace mi
{

namespace neuraylib
{

class IBuffer;
class ICanvas;
class IReader;
class ITile;

/** \if IRAY_API \addtogroup mi_neuray_rendering
    \elseif MDL_SDK_API \addtogroup mi_neuray_misc
    \elseif DICE_API \addtogroup mi_neuray_rtmp
    \endif
@{
*/

/// This interface provides various utilities related to canvases and buffers.
///
/// Note that #create_buffer_from_canvas() and #create_canvas_from_buffer() encode and decode pixel
/// data to/from memory buffers. \if IRAY_API To import images from disk use
/// #mi::neuraylib::IImport_api::import_canvas(). To export images to disk use
/// #mi::neuraylib::IExport_api::export_canvas(). \endif
class IImage_api : public mi::base::Interface_declare<0x4c25a4f0, 0x2bac, 0x4ce6, 0xb0, 0xab, 0x4d,
                     0x94, 0xbf, 0xfd, 0x97, 0xa5>
{
public:
  /// \name Factory methods for canvases and tiles
  //@{

  /// Creates a canvas with given pixel type, width, height, and layers.
  ///
  /// This factory function allows to create instances of the abstract interface
  /// #mi::neuraylib::ICanvas based on an internal default implementation. However, you are not
  /// obligated to use this factory function and the internal default implementation. It is
  /// absolutely fine to use your own (correct) implementation of the #mi::neuraylib::ICanvas
  /// interface.
  ///
  /// \param pixel_type   The desired pixel type. See \ref mi_neuray_types for a list of
  ///                     supported pixel types.
  /// \param width        The desired width.
  /// \param height       The desired height.
  /// \param tile_width   The desired tile width. The special value 0 represents the canvas
  ///                     width.
  /// \param tile_height  The desired tile height. The special value 0 represents the canvas
  ///                     height.
  /// \param layers       The desired number of layers (depth). Must be 6 for cubemaps.
  /// \param is_cubemap   Flag that indicates whether this canvas represents a cubemap.
  /// \param gamma        The desired gamma value. The special value 0.0 represents the default
  ///                     gamma which is 1.0 for HDR pixel types and 2.2 for LDR pixel types.
  /// \return             The requested canvas, or \c NULL in case of invalid pixel type, width,
  ///                     height, layers, or cubemap flag.
  virtual ICanvas* create_canvas(const char* pixel_type, Uint32 width, Uint32 height,
    Uint32 tile_width = 0, Uint32 tile_height = 0, Uint32 layers = 1, bool is_cubemap = false,
    Float32 gamma = 0.0f) const = 0;

  /// Creates a tile with given pixel type, width, and height.
  ///
  /// This factory function allows to create instances of the abstract interface
  /// #mi::neuraylib::ITile based on an internal default implementation. However, you are not
  /// obligated to use this factory function and the internal default implementation. It is
  /// absolutely fine to use your own (correct) implementation of the #mi::neuraylib::ITile
  /// interface.
  ///
  /// \param pixel_type   The desired pixel type. See \ref mi_neuray_types for a list of supported
  ///                     pixel types.
  /// \param width        The desired width.
  /// \param height       The desired height.
  /// \return             The requested tile, or \c NULL in case of invalid pixel type, width, or
  ///                     height.
  virtual ITile* create_tile(const char* pixel_type, Uint32 width, Uint32 height) const = 0;

  //@}
  /// \name Conversion between canvases and raw memory buffers
  //@{

  /// Reads raw pixel data from a canvas.
  ///
  /// Reads a rectangular area of pixels from a canvas (possibly spanning multiple tiles),
  /// converts the pixel type if needed, and writes the pixel data to buffer in memory.
  /// Management of the buffer memory is the responsibility of the caller.
  ///
  /// \param width               The width of the rectangular pixel area.
  /// \param height              The height of the rectangular pixel area.
  /// \param canvas              The canvas to read the pixel data from.
  /// \param canvas_x            The x-coordinate of the lower-left corner of the rectangle.
  /// \param canvas_y            The y-coordinate of the lower-left corner of the rectangle.
  /// \param canvas_layer        The layer of the canvas that holds the rectangular area.
  /// \param buffer              The buffer to write the pixel data to.
  /// \param buffer_topdown      Indicates whether the buffer stores the rows in top-down order.
  /// \param buffer_pixel_type   The pixel type of the buffer. See \ref mi_neuray_types for a
  ///                            list of supported pixel types.
  /// \param buffer_padding      The padding between subsequent rows of the buffer in bytes.
  /// \return
  ///                            -  0: Success.
  ///                            - -1: Invalid parameters (\c NULL pointer).
  ///                            - -2: \p width or \p height is zero.
  ///                            - -3: Invalid pixel type of the buffer.
  ///                            - -4: The rectangular area [\p canvas_x, \p canvas_x + \p width)
  ///                                  x [\p canvas_y, \p canvas_y + \p height) exceeds the size
  ///                                  of the canvas, or \p canvas_layer is invalid.
  virtual Sint32 read_raw_pixels(Uint32 width, Uint32 height, const ICanvas* canvas,
    Uint32 canvas_x, Uint32 canvas_y, Uint32 canvas_layer, void* buffer, bool buffer_topdown,
    const char* buffer_pixel_type, Uint32 buffer_padding = 0) const = 0;

  /// Writes raw pixel data to a canvas.
  ///
  /// Reads a rectangular area of pixels from a buffer in memory, converts the pixel type if
  /// needed, and writes the pixel data to a canvas (possibly spanning multiple tiles).
  /// Management of the buffer memory is the responsibility of the caller.
  ///
  /// \param width               The width of the rectangular pixel area.
  /// \param height              The height of the rectangular pixel area.
  /// \param canvas              The canvas to write the pixel data to.
  /// \param canvas_x            The x-coordinate of the lower-left corner of the rectangle.
  /// \param canvas_y            The y-coordinate of the lower-left corner of the rectangle.
  /// \param canvas_layer        The layer of the canvas that holds the rectangular area.
  /// \param buffer              The buffer to read the pixel data from.
  /// \param buffer_topdown      Indicates whether the buffer stores the rows in top-down order.
  /// \param buffer_pixel_type   The pixel type of the buffer. See \ref mi_neuray_types for a
  ///                            list of supported pixel types.
  /// \param buffer_padding      The padding between subsequent rows of the buffer in bytes.
  /// \return
  ///                            -  0: Success.
  ///                            - -1: Invalid parameters (\c NULL pointer).
  ///                            - -2: \p width or \p height is zero.
  ///                            - -3: Invalid pixel type of the buffer.
  ///                            - -4: The rectangular area [\p canvas_x, \p canvas_x + \p width)
  ///                                  x [\p canvas_y, \p canvas_y + \p height) exceeds the size
  ///                                  of the canvas, or \p canvas_layer is invalid.
  virtual Sint32 write_raw_pixels(Uint32 width, Uint32 height, ICanvas* canvas, Uint32 canvas_x,
    Uint32 canvas_y, Uint32 canvas_layer, const void* buffer, bool buffer_topdown,
    const char* buffer_pixel_type, Uint32 buffer_padding = 0) const = 0;

  //@}
  /// \name Conversion between canvases and encoded images
  //@{

  /// Encodes the pixel data of a canvas into a memory buffer.
  ///
  /// \param canvas        The canvas whose contents are to be used.
  /// \param image_format  The desired image format of the image, e.g., \c "jpg". Note that
  ///                      support for a given image format requires an image plugin capable of
  ///                      handling that format.
  /// \param pixel_type    The desired pixel type. See \ref mi_neuray_types for a list of
  ///                      supported pixel types. Not every image plugin supports every pixel
  ///                      type. If the requested pixel type is not supported, the argument is
  ///                      ignored and one of the supported formats is chosen instead.
  /// \param quality       The compression quality is an integer in the range from 0 to 100, where
  ///                      0 is the lowest quality, and 100 is the highest quality.
  /// \return              The created buffer, or \c NULL in case of failure.
  virtual IBuffer* create_buffer_from_canvas(const ICanvas* canvas, const char* image_format,
    const char* pixel_type, const char* quality) const = 0;

  /// Decodes the pixel data of a memory buffer into a canvas.
  ///
  /// \param buffer        The buffer that holds the encoded pixel data.
  /// \param image_format  The image format of the buffer, e.g., \c "jpg". Note that
  ///                      support for a given image format requires an image plugin capable of
  ///                      handling that format.
  /// \return              The canvas with the decoded pixel data, or \c NULL in case of failure.
  virtual ICanvas* create_canvas_from_buffer(
    const IBuffer* buffer, const char* image_format) const = 0;

  /// Indicates whether a particular image format is supported for decoding.
  ///
  /// Support for a given image format requires an image plugin capable of handling that format.
  /// This method allows to check whether such a plugin has been loaded for a particular format.
  ///
  /// Decoding is used when the image is converted into a canvas from a \if DICE_API memory
  /// buffer. \else memory buffer or a file \endif. Note that even if this method returns \c true,
  /// #create_canvas_from_buffer() \if IRAY_API or
  /// #mi::neuraylib::IImport_api::import_canvas() \endif can still fail for a particular image if
  /// that image uses an unsupported feature.
  ///
  /// \param image_format   The image format in question, e.g., \c "jpg".
  /// \param reader         An optional reader \if IRAY_API used by
  ///                       #mi::neuraylib::IImage_plugin::test(). \endif
  /// \return               \c true if the image format is supported, \c false otherwise
  virtual bool supports_format_for_decoding(
    const char* image_format, IReader* reader = 0) const = 0;

  /// Indicates whether a particular image format is supported for encoding.
  ///
  /// Support for a given image format requires an image plugin capable of handling that format.
  /// This method allows to check whether such a plugin has been loaded for a particular format.
  ///
  /// Encoding is used when the image is converted from a canvas into a \if DICE_API memory
  /// buffer. \else memory buffer or a file. \endif. Note that even if this method returns
  /// \c true, #create_buffer_from_canvas() \if IRAY_API or
  /// #mi::neuraylib::IExport_api::export_canvas \endif can still fail if the given canvas
  /// uses an unsupported feature, e.g., multiple layers.
  ///
  /// \param image_format   The image format in question, e.g., \c "jpg".
  /// \return               \c true if the image format is supported, \c false otherwise
  virtual bool supports_format_for_encoding(const char* image_format) const = 0;

  //@}
  /// \name Utility methods for canvases
  //@{

  /// Converts a canvas to a different pixel type.
  ///
  /// \note This method creates a copy if the passed-in canvas already has the desired pixel type.
  /// (It cannot return the passed-in canvas since this would require a const cast.) If
  /// performance is critical, you should compare pixel types yourself and skip the method call if
  /// pixel type conversion is not needed.)
  ///
  /// The conversion converts a given pixel as follows:
  ///
  /// - Floating-point values are linearly mapped to integers as follows: 0.0f is mapped to 0 and
  ///   1.0f is mapped to 255 or 65535, respectively. Note that the pixel type \c "Sint8" is
  ///   treated as the corresponding unsigned integer type \c "Uint8" here. Floating-point values
  ///   are clamped to [0.0f, 1.0f] beforehand. The reverse conversion uses the corresponding
  ///   inverse mapping.
  /// - Single-channel formats are converted to grey-scale RGB formats by duplicating the value
  ///   in each channel.
  /// - RGB formats are converted to single-channel formats by mixing the RGB channels with
  ///   weights 0.27f for red, 0.67f for green, and 0.06f for blue.
  /// - If an alpha channel is added, the values are set to 1.0f, 255, or 65535 respectively.
  /// - The pixel type \c "Float32<4>" is treated in the same way as \c "Color", \c "Float32<3>"
  ///   in the same way as \c "Rgb_fp", and \c "Sint32" in the same way as \c "Rgba".
  /// - The pixel type \c "Rgbe" is converted via \c "Rgb_fp". Similarly, \c "Rgbea" is converted
  ///   via \c "Color".
  /// - \c "Float32<2>" is converted to single-channel formats by averaging the two channels. If
  ///   \c "Float32<2>" is converted to three- or four-channel formats, the blue channel is set to
  ///   0.0f, or 0, respectively. Conversion of single-channel formats to \c "Float32<2>"
  ///   duplicates the channel. Conversion of three- or four-channel formats to \c "Float32<2>"
  ///   drops the third and fourth channel.
  ///
  /// \param canvas       The canvas to convert (or to copy).
  /// \param pixel_type   The desired pixel type. See \ref mi_neuray_types for a list of supported
  ///                     pixel types. If this pixel type is the same as the pixel type of \p
  ///                     canvas, then a copy of the canvas is returned.
  /// \return             A canvas with the requested pixel type, or \c NULL in case of errors
  ///                     (\p canvas is \c NULL, or \p pixel_type is not valid).
  virtual ICanvas* convert(const ICanvas* canvas, const char* pixel_type) const = 0;

  /// Sets the gamma value of a canvas and adjusts the pixel data accordingly.
  ///
  /// \note Gamma adjustments are always done in pixel type "Color" or "Rgb_fp". If necessary,
  ///       the pixel data is converted forth and back automatically (which needs temporary
  ///       buffers).
  ///
  /// \param canvas           The canvas whose pixel data is to be adjusted.
  /// \param new_gamma        The new gamma value.
  virtual void adjust_gamma(ICanvas* canvas, Float32 new_gamma) const = 0;

  //@}
  /// \name Utility methods for pixel type characteristics
  //@{

  /// Returns the number of components per pixel type.
  ///
  /// For example, for the pixel type "Color" the method returns 4 because it consists of four
  /// components R, G, B, and A. Returns 0 in case of invalid pixel types.
  ///
  /// \see #get_bytes_per_component()
  virtual Uint32 get_components_per_pixel(const char* pixel_type) const = 0;

  /// Returns the number of bytes used per pixel component.
  ///
  /// For example, for the pixel type "Color" the method returns 4 because its components are of
  /// type #mi::Float32 which needs 4 bytes. Returns 0 in case of invalid pixel types.
  ///
  /// \see #get_components_per_pixel()
  virtual Uint32 get_bytes_per_component(const char* pixel_type) const = 0;

  //@}
};

/*@}*/ // end group mi_neuray_rendering / mi_neuray_rtmp

} // namespace neuraylib

} // namespace mi

#endif // MI_NEURAYLIB_IIMAGE_API_H
