Problem Description
Solution
To create custom images or graphics directly within your MQL4 code without loading external files, you need to use Dynamic Resources.
The process involves three main steps:
- Data Preparation: Create an array of type
uint(unsigned integer) representing the pixels. - Resource Creation: Use
ResourceCreate()to convert that array into a graphical resource in memory. - Display: Create a chart object (usually
OBJ_BITMAP_LABEL) and bind it to that resource.
MQL4 Code Example: Dynamic Graphics Generator
This Expert Advisor demonstrates how to generate a gradient background with a moving circle directly in the code.
//+------------------------------------------------------------------+
//| DynamicGraphicsDemo.mq4 |
//| Copyright 2023, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative AI"
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
//--- Input Parameters
input int CanvasWidth = 300; // Width of the image
input int CanvasHeight = 200; // Height of the image
input color CircleColor = clrRed;// Color of the moving circle
//--- Global Variables
uint g_pixels[]; // The pixel array (buffer)
string g_resourceName = "::MyDynamicImage"; // Resource name must start with "::"
string g_objectName = "MyBitmapLabel"; // Name of the chart object
int g_circleX = 0; // Current X position of circle
int g_circleY = 100; // Current Y position of circle
int g_direction = 5; // Movement speed/direction
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// 1. Resize the pixel array to fit the dimensions
ArrayResize(g_pixels, CanvasWidth * CanvasHeight);
// 2. Create the chart object to display the image
if(!ObjectCreate(0, g_objectName, OBJ_BITMAP_LABEL, 0, 0, 0))
{
Print("Failed to create object");
return(INIT_FAILED);
}
// 3. Configure Object Properties
ObjectSetInteger(0, g_objectName, OBJPROP_XDISTANCE, 50); // 50 pixels from left
ObjectSetInteger(0, g_objectName, OBJPROP_YDISTANCE, 50); // 50 pixels from top
ObjectSetInteger(0, g_objectName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, g_objectName, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
// 4. Bind the object to the dynamic resource name
// Note: The resource doesn't exist yet, but we bind the name now.
ObjectSetString(0, g_objectName, OBJPROP_BMPFILE, g_resourceName);
// 5. Enable timer for animation (every 50 milliseconds)
EventSetMillisecondTimer(50);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
EventKillTimer();
// Clean up the object
ObjectDelete(0, g_objectName);
// Free the memory used by the resource
ResourceFree(g_resourceName);
}
//+------------------------------------------------------------------+
//| Timer function (Animation Loop) |
//+------------------------------------------------------------------+
void OnTimer()
{
// Update logic: Move the circle back and forth
g_circleX += g_direction;
if(g_circleX > CanvasWidth - 20 || g_circleX < 20) g_direction *= -1;
// Redraw the scene
DrawScene();
}
//+------------------------------------------------------------------+
//| Function to draw pixels into the array and update resource |
//+------------------------------------------------------------------+
void DrawScene()
{
// --- Step A: Clear the background (Fill with a gradient) ---
for(int y = 0; y < CanvasHeight; y++)
{
for(int x = 0; x < CanvasWidth; x++)
{
// Calculate array index: y * width + x
int i = y * CanvasWidth + x;
// Create a gradient color based on X position
// Alpha (transparency) is the highest byte (0xFF = fully opaque)
uchar alpha = 255;
uchar red = (uchar)((double)x / CanvasWidth * 255.0);
uchar green = (uchar)((double)y / CanvasHeight * 255.0);
uchar blue = 50;
// Combine channels into one uint color (ARGB format)
g_pixels[i] = ColorToARGB(C'0,0,0', alpha) | (red << 16) | (green << 8) | blue;
}
}
// --- Step B: Draw a Circle ---
int radius = 20;
uint circleColorUint = ColorToARGB(CircleColor, 255); // Convert MQL color to ARGB
for(int y = -radius; y <= radius; y++)
{
for(int x = -radius; x <= radius; x++)
{
if(x*x + y*y <= radius*radius) // Circle equation
{
int drawX = g_circleX + x;
int drawY = g_circleY + y;
// Boundary checks to prevent array out of range
if(drawX >= 0 && drawX < CanvasWidth && drawY >= 0 && drawY < CanvasHeight)
{
int i = drawY * CanvasWidth + drawX;
g_pixels[i] = circleColorUint;
}
}
}
}
// --- Step C: Update the Resource ---
// This pushes the pixel array into the resource memory
if(!ResourceCreate(g_resourceName, g_pixels, CanvasWidth, CanvasHeight, 0, 0, 0, COLOR_FORMAT_ARGB_NORMALIZE))
{
Print("ResourceCreate failed");
return;
}
// --- Step D: Force Chart Redraw ---
ChartRedraw();
}
Key Concepts Explained
1. The Pixel Buffer (uint[])
Images in MQL4 are essentially 1-dimensional arrays of unsigned integers.
- Size:
Width * Height. - Indexing: To access the pixel at coordinates
(x, y), the formula isindex = y * Width + x.
2. Color Format (ARGB)
Standard MQL4 colors (like clrRed) are just integers, but for resources, you must explicitly handle the Alpha channel (transparency).
- Format:
0xAARRGGBB(Alpha, Red, Green, Blue). - Helper Function:
ColorToARGB(color_value, alpha_value)converts standard colors to this format.alpha=255: Fully Opaque.alpha=0: Fully Transparent (invisible).
3. ResourceCreate
This is the engine of the operation.
- Name: Must start with
::(e.g.,"::MyImage"). This tells MT4 it is a dynamic resource in memory, not a file on the disk. - Update: Calling
ResourceCreaterepeatedly with the same name updates the image. This allows for animation (as shown in theOnTimerfunction).
4. OBJ_BITMAP_LABEL
This is the graphical object used to display the resource.
- You set its
OBJPROP_BMPFILEproperty to the resource name ("::MyImage"). - Unlike
OBJ_LABEL(which is text), this object renders raw pixel data.
Q&A
Q: Can I load an image from the disk and then modify it?
A: Yes. You can use ResourceReadImage to load a .bmp file into a uint array, modify the pixels in the array using the logic above, and then save it back using ResourceCreate.
Q: Why do colors look wrong when I manually set the integer?
A: You might be mixing up RGB and BGR, or forgetting the Alpha channel. If the Alpha channel is 0, the pixel will be invisible. Always use ColorToARGB or ensure you bitwise shift the Alpha byte (0xFF << 24) correctly.
Q: Is this CPU intensive?
A: It can be. Iterating through 60,000 pixels (300x200) every 50ms is generally fine, but generating full-screen HD graphics (1920x1080) in MQL4 on every tick will lag the terminal. Keep your canvas size reasonable.