Download MetaTrader 5
To add comments, please log in or register
Have you made a screenshot in MetaTrader? Share it with friends!
smimon
127
smimon 2015.12.22 09:49 

This is just some information, rather than a question, but I thought I'd share it since I've been wondering how to do this for some time.

The standard BITMAP and BITMAP_LABEL objects allow us to load bitmap images on to the chart, but do not support 32-bit images (with alpha channel).

This means if you want to load an image that has a transparent background, you have to first paint the background of the image the same colour as the background of your chart in a drawing program. This subsequently means if you are coding an indicator that you want to share with others, if they have a different background colour to their chart than you, it's going to show up as an ugly square of the incorrect colour.

By way of an example:

 Original image with transparent background

 Painted with my chart's background colour

 How it looks on my chart

 How it looks on someone else's chart with a white background

 Not to mention that if the image ends up in front of candles even on my chart, it still looks ugly

 

This is where the Canvas class can come to the rescue. It had a method called LoadFromFile, which supports 32-bit bitmaps.

The syntax is simple:

CCanvas canvas();
canvas.CreateBitmapLabel(chartId, subwindowId, chartObjectName, x, y, width, height, COLOR_FORMAT_ARGB_RAW);
canvas.LoadFromFile(file);
canvas.Update();

 

First however we must convert our bitmap to 32-bit alpha format.

There is a free program called Pixelformer which enables us to do this easily. Images can be imported in PNG format with transparency, and exported as 32-bit bitmaps. There is one small caveat - in order to display properly in MT4, the bitmap must be exported from Pixelformer with the "top-down row order" option selected. If this is not checked, only the top half of the bitmap shows up when you try to display it in MT4 (I think this is down to a bug in the section of the Canvas code which tries to flip an image when handling 32-bit format).

 

 

Once this has been done, the result can be seen as follows:

  Arrow displays correctly on dark background

   And against a light background

 And can be superimposed in front of candles without obscuring them

Doerk Hilger
1074
Doerk Hilger 2015.12.24 14:24  

If you replace the function LoadFromFile() by the fixed code, you will also be able to load images that were stored as resources. But you must not set the Top-Down-row-order flag, it won´t work.

This way you don´t need to copy the bitmaps into the files folder of any clients.

Enjoy.

Doerk

//+------------------------------------------------------------------+
//| Load data from file                                              |
//+------------------------------------------------------------------+
bool CCanvas::LoadFromFile(const string filename)
  {
   struct BitmapHeader
     {
      ushort            type;
      uint              size;
      uint              reserv;
      uint              offbits;
      uint              imgSSize;
      uint              imgWidth;
      uint              imgHeight;
      ushort            imgPlanes;
      ushort            imgBitCount;
      uint              imgCompression;
      uint              imgSizeImage;
      uint              imgXPelsPerMeter;
      uint              imgYPelsPerMeter;
      uint              imgClrUsed;
      uint              imgClrImportant;
     }      header;
   bool     result=true;
   CFileBin file;
   int      x,y;
   uchar    a,r,g,b;
   uchar    tmp[];
   uint     img_size;
   bool     no_alpha,no_flip=false;
//--- open file
   if(file.Open(filename,FILE_READ)==INVALID_HANDLE)
     {
      //--- Modified by Doerk
      if (!ResourceReadImage(filename,m_pixels,m_width,m_height))
         return(false);
      else
         return (true);
     }
//--- read header
   if(file.ReadStruct(header)!=sizeof(header))
     {
      Print("Failed to read file header");
      file.Close();
      return(false);
     }
   m_width =(int)header.imgWidth;
   m_height=(int)header.imgHeight;
   if(m_height<0)
     {
      m_height=-m_height;
      no_flip=true;
     }
//--- process depending on color depth
   if(header.imgBitCount==32)
     {
      no_alpha=true;
      img_size=file.ReadArray(m_pixels);
      //--- flip image
      if(!no_flip)
         for(y=0;y<m_height/2;y++)
           {
            ArrayCopy(tmp,m_pixels,0,m_width*y,m_width);
            ArrayCopy(m_pixels,m_pixels,m_width*y,m_width*(m_height-y-1),m_width);
            ArrayCopy(m_pixels,tmp,m_width*(m_height-y-1),0,m_width);
           }
      //--- check if at least one pixel has alpha channel
      //--- then leave image as is (consider it as premultiplied ARGB)
      uint i;
      for(i=0;i<img_size;i++)
        {
         //--- there is alpha channel
         if(GETRGBA(m_pixels[i])!=0)
           {
            no_alpha=false;
            break;
           }
        }
      //--- no alpha channel
      if(no_alpha)
        {
         //--- consider image as nontransparent, add alpha channel as 0xFF
         for(i=0;i<img_size;i++)
            m_pixels[i]|=0xFF000000;
        }
      else
        {
         if(m_format==COLOR_FORMAT_ARGB_RAW)
           {
            //--- color components are not processed by terminal (they should be correctly specified by user) 
            //--- convert image to premultiplied ARGB
            for(i=0;i<img_size;i++)
              {
               switch(a=GETRGBA(m_pixels[i]))
                 {
                  case 0xFF:
                     break;
                  case 0x00:
                     m_pixels[i]=0;
                     break;
                  default:
                     r=GETRGBR(m_pixels[i])*a/255;
                     g=GETRGBG(m_pixels[i])*a/255;
                     b=GETRGBB(m_pixels[i])*a/255;
                     m_pixels[i]=ARGB(a,r,g,b);
                     break;
                 }
              }
           }
        }
     }
   else
     {
      //--- 24 bits - change image color depth to 32 bits
      int byte_width;
      //--- allocate memory for pixels
      if(ArrayResize(m_pixels,m_width*m_height)!=-1)
        {
         //--- the number of bytes that define a line of pixels must be multiple of 4
         byte_width=m_width*3;             // number of bytes in line of pixels
         byte_width=(byte_width+3)&~3;     // align line to the 4 byte boundary
         for(y=0;y<m_height;y++)
           {
            if(file.ReadArray(tmp,0,byte_width)!=byte_width)
              {
               result=false;
               break;
              }
            int k,p;
            for(x=0,k=0,p=m_width*(m_height-y-1);x<m_width;x++,p++,k+=3)
              {
               r=tmp[k+2];
               g=tmp[k+1];
               b=tmp[k];
               m_pixels[p]=XRGB(r,g,b);
              }
           }
        }
      else
         result=false;
     }
//--- succeed
   file.Close();
   return(result);
  }
xtrader68
3
xtrader68 2016.09.22 15:04  

Hallo and thank you for this publication,

I have tried the functions. The function to cerate ab BitmapLabel is running,  but the image is not load.

I can see the Object in the Properties on chart and I can select it.

Now I controlled the File to the bmp and copy it for test in same Folder "Expert"

The bmp ist 32bit and I used Pixelformer and same format too.

I hope someone can help me... thank you.



#include "Canvas.mqh"

CCanvas canvas();

canvas.CreateBitmapLabel(0,0,"testbild",50,50,50,50,COLOR_FORMAT_ARGB_RAW);
canvas.LoadFromFile("testbild.bmp");

//string test = canvas.LoadFromFile("testbild.bmp");
//Alert (test); ///////////------------------->>>>>>>>>>>>>>>>>>>OUTPUT IS "FALSE"

canvas.Update();

ChartRedraw(); >>>>>>>>>>> ONLY FOR TEST
JPS1
239
JPS1 2016.09.29 11:19  
xtrader68:

Hallo and thank you for this publication,

I have tried the functions. The function to cerate ab BitmapLabel is running,  but the image is not load...


Canvas #include is not necessary for bitmap image with alpha channel.

Bitmaps with alpha channel require #resource followed by path to image or they will not display. This is not found in MQL4 documentation but it's the only way it will work. 

Example - If your image is located inside the folder /MQL4/Files/, then you would use the following: #resource"\\Files\\testbild.bmp";

Then add the image to the chart using whatever code you've created then use "::Files\\testbild.bmp" for your path. 

/
To add comments, please log in or register