/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_image_encoders_bmp_nsBMPEncoder_h
#define mozilla_image_encoders_bmp_nsBMPEncoder_h

#include "mozilla/Attributes.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/UniquePtr.h"

#include "imgIEncoder.h"
#include "BMPHeaders.h"

#include "nsCOMPtr.h"

#define NS_BMPENCODER_CID \
{ /* 13a5320c-4c91-4FA4-bd16-b081a3ba8c0b */         \
     0x13a5320c,                                     \
     0x4c91,                                         \
     0x4fa4,                                         \
    {0xbd, 0x16, 0xb0, 0x81, 0xa3, 0Xba, 0x8c, 0x0b} \
}

namespace mozilla {
namespace image {
namespace bmp {

struct FileHeader {
  char signature[2];   // String "BM".
  uint32_t filesize;   // File size.
  int32_t reserved;    // Zero.
  uint32_t dataoffset; // Offset to raster data.
};

struct XYZ {
  int32_t x, y, z;
};

struct XYZTriple {
  XYZ r, g, b;
};

struct V5InfoHeader {
  uint32_t bihsize;          // Header size
  int32_t width;             // Uint16 in OS/2 BMPs
  int32_t height;            // Uint16 in OS/2 BMPs
  uint16_t planes;           // =1
  uint16_t bpp;              // Bits per pixel.
  uint32_t compression;      // See Compression for valid values
  uint32_t image_size;       // (compressed) image size. Can be 0 if
                             // compression==0
  uint32_t xppm;             // Pixels per meter, horizontal
  uint32_t yppm;             // Pixels per meter, vertical
  uint32_t colors;           // Used Colors
  uint32_t important_colors; // Number of important colors. 0=all
  // The rest of the header is not available in WIN_V3 BMP Files
  uint32_t red_mask;         // Bits used for red component
  uint32_t green_mask;       // Bits used for green component
  uint32_t blue_mask;        // Bits used for blue component
  uint32_t alpha_mask;       // Bits used for alpha component
  uint32_t color_space;      // 0x73524742=LCS_sRGB ...
  // These members are unused unless color_space == LCS_CALIBRATED_RGB
  XYZTriple white_point;     // Logical white point
  uint32_t gamma_red;        // Red gamma component
  uint32_t gamma_green;      // Green gamma component
  uint32_t gamma_blue;       // Blue gamma component
  uint32_t intent;           // Rendering intent
  // These members are unused unless color_space == LCS_PROFILE_*
  uint32_t profile_offset;   // Offset to profile data in bytes
  uint32_t profile_size;     // Size of profile data in bytes
  uint32_t reserved;         // =0

  static const uint32_t COLOR_SPACE_LCS_SRGB = 0x73524742;
};

} // namespace bmp
} // namespace image
} // namespace mozilla

// Provides BMP encoding functionality. Use InitFromData() to do the
// encoding. See that function definition for encoding options.

class nsBMPEncoder final : public imgIEncoder
{
  typedef mozilla::ReentrantMonitor ReentrantMonitor;
public:
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_IMGIENCODER
  NS_DECL_NSIINPUTSTREAM
  NS_DECL_NSIASYNCINPUTSTREAM

  nsBMPEncoder();

protected:
  ~nsBMPEncoder();

  enum Version
  {
      VERSION_3 = 3,
      VERSION_5 = 5
  };

  // See InitData in the cpp for valid parse options
  nsresult ParseOptions(const nsAString& aOptions, Version& aVersionOut,
                        uint16_t& aBppOut);
  // Obtains data with no alpha in machine-independent byte order
  void ConvertHostARGBRow(const uint8_t* aSrc,
                          const mozilla::UniquePtr<uint8_t[]>& aDest,
                          uint32_t aPixelWidth);
  // Thread safe notify listener
  void NotifyListener();

  // Initializes the bitmap file header member mBMPFileHeader
  nsresult InitFileHeader(Version aVersion, uint16_t aBPP, uint32_t aWidth,
                          uint32_t aHeight);
  // Initializes the bitmap info header member mBMPInfoHeader
  nsresult InitInfoHeader(Version aVersion, uint16_t aBPP, uint32_t aWidth,
                          uint32_t aHeight);

  // Encodes the bitmap file header member mBMPFileHeader
  void EncodeFileHeader();
  // Encodes the bitmap info header member mBMPInfoHeader
  void EncodeInfoHeader();
  // Encodes a row of image data which does not have alpha data
  void EncodeImageDataRow24(const uint8_t* aData);
  // Encodes a row of image data which does have alpha data
  void EncodeImageDataRow32(const uint8_t* aData);
  // Obtains the current offset filled up to for the image buffer
  inline int32_t GetCurrentImageBufferOffset()
  {
    return static_cast<int32_t>(mImageBufferCurr - mImageBufferStart);
  }

  // These headers will always contain endian independent stuff
  // They store the BMP headers which will be encoded
  mozilla::image::bmp::FileHeader mBMPFileHeader;
  mozilla::image::bmp::V5InfoHeader mBMPInfoHeader;

  // Keeps track of the start of the image buffer
  uint8_t* mImageBufferStart;
  // Keeps track of the current position in the image buffer
  uint8_t* mImageBufferCurr;
  // Keeps track of the image buffer size
  uint32_t mImageBufferSize;
  // Keeps track of the number of bytes in the image buffer which are read
  uint32_t mImageBufferReadPoint;
  // Stores true if the image is done being encoded
  bool mFinished;

  nsCOMPtr<nsIInputStreamCallback> mCallback;
  nsCOMPtr<nsIEventTarget> mCallbackTarget;
  uint32_t mNotifyThreshold;
};

#endif // mozilla_image_encoders_bmp_nsBMPEncoder_h