//+------------------------------------------------------------------+
//|                                                      FileBMP.mqh |
//|                             Copyright 2000-2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include <Object.mqh>

//+------------------------------------------------------------------+
//| Bitmap headers                                                   |
//+------------------------------------------------------------------+
struct BITMAPFILEHEADER
  {
   ushort            bfType;
   uint              bfSize;
   ushort            bfReserved1;
   ushort            bfReserved2;
   uint              bfOffBits;
  };
struct BITMAPINFOHEADER
  {
   uint              biSize;
   int               biWidth;
   int               biHeight;
   ushort            biPlanes;
   ushort            biBitCount;
   uint              biCompression;
   uint              biSizeImage;
   int               biXPelsPerMeter;
   int               biYPelsPerMeter;
   uint              biClrUsed;
   uint              biClrImportant;
  };
#define BM 0x4D42
//+------------------------------------------------------------------+
//| Class CFileBMP                                                   |
//| Purpose: Special class to read and write bmp file                |
//|          Derives from class CObject.                             |
//+------------------------------------------------------------------+
class CFileBMP : public CObject
  {
protected:
   int               m_handle;
   BITMAPFILEHEADER  m_file_header;
   BITMAPINFOHEADER  m_info_header;

public:
                     CFileBMP(void);
                    ~CFileBMP(void);
   int               OpenWrite(const string file_name,bool common_flag=false);
   int               OpenRead(const string file_name,bool common_flag=false);
   int               Write32BitsArray(uint& uint_array[],const int width,const int height);
   int               Read32BitsArray(uint& uint_array[],int& width,int& height);
   void              Close(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CFileBMP::CFileBMP(void) : m_handle(INVALID_HANDLE)
  {
   ZeroMemory(m_file_header);
   ZeroMemory(m_info_header);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CFileBMP::~CFileBMP(void)
  {
   Close();
  }
//+------------------------------------------------------------------+
//| Open the file                                                    |
//+------------------------------------------------------------------+
int CFileBMP::OpenWrite(const string file_name,bool common_flag)
  {
   Close();
//--- action
   int open_flags=FILE_BIN|FILE_WRITE|FILE_SHARE_READ|FILE_SHARE_WRITE;
   if(common_flag)
      open_flags|=FILE_COMMON;
//--- open
   m_handle=FileOpen(file_name,open_flags);
//--- result
   return(m_handle);
  }
//+------------------------------------------------------------------+
//| Open the file                                                    |
//+------------------------------------------------------------------+
int CFileBMP::OpenRead(const string file_name,bool common_flag)
  {
   Close();
//--- action
   int open_flags=FILE_BIN|FILE_READ|FILE_SHARE_READ|FILE_SHARE_WRITE;
   if(common_flag)
      open_flags|=FILE_COMMON;
//--- open
   m_handle=FileOpen(file_name,open_flags);
//--- check bmp headers
   if(m_handle!=INVALID_HANDLE)
     {
      uint fileheader_size=FileReadStruct(m_handle,m_file_header);
      uint infoheader_size=FileReadStruct(m_handle,m_info_header);
      //--- it should be a simple 32-bit bmp
      if(fileheader_size!=sizeof(m_file_header) ||
         infoheader_size!=sizeof(m_info_header) ||
         m_file_header.bfType!=BM ||
         m_file_header.bfOffBits!=sizeof(m_file_header)+sizeof(m_info_header) ||
         m_info_header.biBitCount!=32 ||
         m_info_header.biClrUsed!=0)
         Close();
     }
//--- result
   return(m_handle);
  }
//+------------------------------------------------------------------+
//| Write the file                                                   |
//+------------------------------------------------------------------+
int CFileBMP::Write32BitsArray(uint& uint_array[],const int width,const int height)
  {
   if(m_handle==INVALID_HANDLE)
      return(-1);
//--- check size
   int size=width*height;
   if(size==0)
      return(0);
   if(size<0)
      size=-size;
   if(ArraySize(uint_array)<size)
      return(-2);
//--- prepare headers
   ZeroMemory(m_file_header);
   ZeroMemory(m_info_header);
   m_file_header.bfType=BM;
   m_file_header.bfSize=sizeof(m_file_header)+sizeof(m_info_header)+size*sizeof(uint);
   m_file_header.bfOffBits=sizeof(m_file_header)+sizeof(m_info_header);
   m_info_header.biSize=sizeof(m_info_header);
   m_info_header.biWidth=width;
   m_info_header.biHeight=height;
   m_info_header.biPlanes=1;
   m_info_header.biBitCount=32;
   m_info_header.biSizeImage=size*32;
//--- write bmp-file
   FileSeek(m_handle,0,SEEK_SET);
   FileWriteStruct(m_handle,m_file_header);
   FileWriteStruct(m_handle,m_info_header);
   uint written=FileWriteArray(m_handle,uint_array,0,size);
//--- bytes written
   return((int)written*sizeof(uint));
  }
//+------------------------------------------------------------------+
//| Read the file                                                    |
//+------------------------------------------------------------------+
int CFileBMP::Read32BitsArray(uint& uint_array[],int& width,int& height)
  {
   if(m_handle==INVALID_HANDLE)
      return(-1);
//--- store dimensions from header
   width=m_info_header.biWidth;
   height=m_info_header.biHeight;
//--- check size
   int size=width*height;
   if(size==0)
      return(0);
   if(size<0)
      size=-size;
//--- read bmp-file
   FileSeek(m_handle,sizeof(m_file_header)+sizeof(m_info_header),SEEK_SET);
   uint read=FileReadArray(m_handle,uint_array,0,size);
//--- bytes read
   return((int)read*sizeof(uint));
  }
//+------------------------------------------------------------------+
//| Close the file                                                   |
//+------------------------------------------------------------------+
void CFileBMP::Close(void)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
     {
      FileClose(m_handle);
      m_handle=INVALID_HANDLE;
     }
  }
//+------------------------------------------------------------------+
