Viewing the resource: Precision Image Scaling via Bicubic Interpolation in MQL5

Precision Image Scaling via Bicubic Interpolation in MQL5

Allan Munene Mutiiria 2025-06-21 23:23:09 86 Views
This article is meticulously crafted to deliver a professional, engaging, and seamless narrative, fl...

Introduction

Envision yourself as a master engineer in a state-of-the-art workshop, where your MetaTrader 5 chart is a high-resolution display poised for a transformative upgrade. The Image Scaling via Bicubic Interpolation EA is your precision engineering toolkit, designed to embed a custom image—such as a trading brand’s logo—onto your chart with superior visual quality. Leveraging bicubic interpolation, a sophisticated algorithm that ensures smooth, high-fidelity scaling, the EA loads a BMP image from a resource file, scales it to fit the chart’s dimensions while preserving aspect ratio, and offers flexible placement options, such as dynamic centering or corner anchoring. Configurable inputs like "LimitToOriginalSize", "CenterImageDynamically", and "AnchorCorner" provide granular control, like calibrating a high-tech system for optimal performance. This non-trading EA focuses on visual enhancement, complementing other trading strategies by creating a professional, branded chart environment, ideal for traders who demand a workspace that reflects technical excellence.

In this article, I’ll guide you through the code with a continuous, professional narrative that flows seamlessly, weaving each phase into a cohesive technical journey that engages and educates. I’ll explain every component with the clarity of a mentor instructing an apprentice, using precise language, technical metaphors, and real-world examples—like enhancing a EURUSD chart with a crisp logo—to bring the concepts to life. We’ll explore how the EA loads, scales, positions, and displays the image, handles chart resizes, and ensures reliability, ensuring you grasp both the mechanics and the engineering brilliance behind each line. With a precision engineering metaphor, this guide will transform the technical into a compelling quest, empowering you to engineer a chart that stands out with clarity and professionalism. Let’s power up the workshop and begin crafting this visual masterpiece!

Strategy Blueprint

Before delving into the code, let’s outline the EA’s technical framework, like drafting a blueprint for a high-precision system:

  • Image Scaling: Embeds a custom BMP image (e.g., “2.bmp”) onto the chart, scaled using bicubic interpolation for smooth, high-quality visuals.

  • Flexible Positioning: Supports dynamic centering ("CenterImageDynamically"=true) or anchoring to a chart corner ("AnchorCorner", e.g., TOP_LEFT) with configurable offsets ("xOffsetFromCorner", "yOffsetFromCorner").

  • Dynamic Resizing: Automatically resizes the image on chart resize events ("CHARTEVENT_CHART_CHANGE"), preserving aspect ratio and quality.

  • Resource Management: Loads the image from a resource file, creates a scaled resource, and manages memory efficiently.

  • Configurability: Inputs like "LimitToOriginalSize" (caps scaling at original dimensions) and "ImageInBackground" (foreground/background toggle) offer precise control.

  • Enhancement Potential: Lacks trading logic but can be paired with trading EAs; custom image paths or opacity settings could be added. This strategy is like an engineer’s blueprint, delivering a high-quality visual enhancement that elevates your trading chart with technical precision.

Code Implementation

Now, let’s enter the engineering workshop and dissect the MQL5 code that powers this visual scaler. I’ll guide you through each phase like a master engineer, ensuring the narrative flows seamlessly with professional rigor and engaging clarity that captivates from start to finish. We’ll cover initializing the image, loading and scaling it with bicubic interpolation, positioning and displaying it, handling chart resizes, and cleaning up, with detailed explanations and examples—like scaling a logo on EURUSD’s chart—to make it accessible for beginners. Each phase will build on the last, crafting a cohesive technical narrative that transforms code into a compelling engineering project. Let’s activate the workshop and begin crafting!

Phase 1: Assembling the Framework—Initializing the Image Resource

Our technical expedition begins by assembling the framework, initializing the image resource to prepare for chart enhancement.

//+------------------------------------------------------------------+
//|                      Image scaling via Bicubic Interpolation.mq5 |
//|                           Copyright 2025, Allan Munene Mutiiria. |
//|                                   https://t.me/Forex_Algo_Trader |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Allan Munene Mutiiria."
#property link      "https://t.me/Forex_Algo_Trader"
#property version   "1.00"

#resource "\\Images\\2.bmp"
#define ORIGINAL_IMAGE_RESOURSE "::Images\\2.bmp"
#define CHART_IMAGE_OBJECT_NAME "ChartImage"

enum ENUM_ANCHOR_CORNER {
   TOP_LEFT = 0, TOP_RIGHT = 1, BOTTOM_LEFT = 2, BOTTOM_RIGHT = 3
};

input bool LimitToOriginalSize = true;
input bool ImageInBackground = true;
input bool CenterImageDynamically = true;
input ENUM_ANCHOR_CORNER AnchorCorner = TOP_LEFT;
input int xOffsetFromCorner = 100;
input int yOffsetFromCorner = 100;

int scaled_resource_counter = 0;

int OnInit(){
   if (!DispalyImageOnChart()){
      Print("ERR: Failed to display the initial image. Reverting now.");
      return (INIT_FAILED);
   }
   return(INIT_SUCCEEDED);
}

We start by constructing the framework with the #property header, declaring the EA as a creation by Allan in 2025 with a Telegram link for support, like unveiling a blueprint for a high-tech system. The #resource directive includes the image file (“2.bmp”) at “\Images\2.bmp”, defined as "ORIGINAL_IMAGE_RESOURSE", like selecting a core component. The "CHART_IMAGE_OBJECT_NAME" constant (“ChartImage”) names the chart object, like labeling a critical module.

The enum ENUM_ANCHOR_CORNER defines positioning options (TOP_LEFT, TOP_RIGHT, etc.), like specifying mounting points for a display. Inputs like "LimitToOriginalSize"=true (caps scaling at original dimensions), "ImageInBackground"=true (background/foreground toggle), "CenterImageDynamically"=true, and "AnchorCorner"=TOP_LEFT with offsets ("xOffsetFromCorner", "yOffsetFromCorner"=100) provide configuration flexibility, like adjustable settings on a precision machine. The "scaled_resource_counter" tracks scaled resource names, ensuring uniqueness, like serial numbers for components.

In "OnInit()", we initiate the process by calling "DispalyImageOnChart()", which handles image loading and display. If it fails, we log an error with "Print()" (“ERR: Failed to display the initial image”) and abort with "INIT_FAILED", ensuring system integrity, like a quality check before assembly. Returning INIT_SUCCEEDED signals, “The framework is operational, ready to enhance!” This streamlined setup primes the EA to scale and display the image, like preparing a workshop for precision engineering.

Phase 2: Engineering the Component—Loading and Scaling the Image

With the framework in place, we load and scale the image using bicubic interpolation, like machining a component to exact specifications.

bool DispalyImageOnChart(){
   uint image_pixels[];
   uint original_image_width, original_image_height;
   
   if (!ResourceReadImage(ORIGINAL_IMAGE_RESOURSE,image_pixels,original_image_width,original_image_height)){
      Print("ERR: FAILED TO READ THE ORIGINAL DATA FROM RESOURCE");
      return false;
   }
   
   int chart_pixel_width = (int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS);
   int chart_pixel_height = (int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS);
   
   double image_aspect_ratio = (double)original_image_width/original_image_height;
   double chart_aspect_ratio = (double)chart_pixel_width/chart_pixel_height;
   int scaled_image_width, scaled_image_height;
   
   if (image_aspect_ratio > chart_aspect_ratio){
      scaled_image_width = chart_pixel_width;
      scaled_image_height = (int)(chart_pixel_width/image_aspect_ratio);
   }
   else {
      scaled_image_width = (int)(chart_pixel_height * image_aspect_ratio);
      scaled_image_height = chart_pixel_height;
   }
   
   if (LimitToOriginalSize){
      scaled_image_width = MathMin(scaled_image_width, (int)original_image_width);
      scaled_image_height = MathMin(scaled_image_height, (int)original_image_height);
      if (scaled_image_width < scaled_image_height * image_aspect_ratio){
         scaled_image_height = (int)(scaled_image_width/image_aspect_ratio);
      }
      else {
         scaled_image_width = (int)(scaled_image_height*image_aspect_ratio);
      }
   }
   
   PrintFormat("Original: %dx%d, Chart: %dx%d, Scaled: %dx%d",
      original_image_width,original_image_height,
      chart_pixel_width,chart_pixel_height,
      scaled_image_width,scaled_image_height);
   
   ScaleImage(image_pixels,original_image_width,original_image_height,scaled_image_width,scaled_image_height);
   
   string scaled_resource_name = "::ScaledImage"+IntegerToString(scaled_resource_counter++);
   
   if (!ResourceCreate(scaled_resource_name,
      image_pixels,scaled_image_width,scaled_image_height,
      0,0,scaled_image_width,COLOR_FORMAT_ARGB_NORMALIZE)){
      Print("ERR: Failed to create the resource with name: ",scaled_resource_name);
      return false;
   }
   
   int x_offset, y_offset;
   if (CenterImageDynamically){
      x_offset = (chart_pixel_width - scaled_image_width)/2;
      y_offset = (chart_pixel_height - scaled_image_height)/2;
   }
   else {
      switch (AnchorCorner){
         case TOP_LEFT: x_offset = xOffsetFromCorner; y_offset = yOffsetFromCorner; break;
         case TOP_RIGHT: x_offset = chart_pixel_width - scaled_image_width - xOffsetFromCorner; y_offset = yOffsetFromCorner; break;
         case BOTTOM_LEFT: x_offset = xOffsetFromCorner; y_offset = chart_pixel_height - scaled_image_height - yOffsetFromCorner; break;
         case BOTTOM_RIGHT: x_offset = chart_pixel_width - scaled_image_width - xOffsetFromCorner; y_offset = chart_pixel_height - scaled_image_height - yOffsetFromCorner; break;
         default: x_offset = xOffsetFromCorner; y_offset = yOffsetFromCorner;
      }
   }
   
   CreateFullChartImage(CHART_IMAGE_OBJECT_NAME,scaled_resource_name,scaled_image_width,scaled_image_height,x_offset,y_offset,ImageInBackground);
   return true;
}

void ScaleImage(uint &pixels[], int original_width, int original_height, int new_width, int new_height){
   uint scaled_pixels[];
   ArrayResize(scaled_pixels, new_width*new_height);
   
   for (int y=0; y<new_height; y++){
      for (int x=0; x<new_width; x++){
         double original_x = (double)x*original_width/new_width;
         double original_y = (double)y*original_height/new_height;
         uint pixel = BicubicInterpolate(pixels,original_width,original_height,original_x,original_y);
         scaled_pixels[y*new_width + x] = pixel;
      }
   }
   ArrayResize(pixels,new_width*new_height);
   ArrayCopy(pixels,scaled_pixels);
}

uint BicubicInterpolate(uint &pixels[], int width, int height, double x, double y){
   int x0 = (int)x; int y0 = (int)y;
   double fractional_x = x-x0; double fractional_y = y-y0;
   
   int x_indices[4], y_indices[4];
   for (int i = -1; i<=2; i++){
      x_indices[i+1] = MathMin(MathMax(x0+i, 0),width - 1);
      y_indices[i+1] = MathMin(MathMax(y0+i, 0),height - 1);
   }
   
   uint neighborhood_pixels[16];
   for (int j=0; j <4; j++){
      for (int i=0; i<4; i++){
         neighborhood_pixels[j*4+i] = pixels[y_indices[j]*width + x_indices[i]];
      }
   }
   
   uchar alpha_components[16], red_components[16], green_components[16], blue_components[16];
   for (int i=0; i<16; i++){
      GetARGB(neighborhood_pixels[i],alpha_components[i],red_components[i],green_components[i],blue_components[i]);
   }
   
   uchar alpha_out = (uchar)BicubicInterpolateComponent(alpha_components,fractional_x,fractional_y);
   uchar red_out = (uchar)BicubicInterpolateComponent(red_components,fractional_x,fractional_y);
   uchar green_out = (uchar)BicubicInterpolateComponent(green_components,fractional_x,fractional_y);
   uchar blue_out = (uchar)BicubicInterpolateComponent(blue_components,fractional_x,fractional_y);
   
   return (alpha_out << 24) | (red_out << 16) | (green_out << 8) | blue_out;
}

double BicubicInterpolateComponent(uchar &components[], double fractional_x, double fractional_y){
   double weights_x[4];
   double t = fractional_x;
   weights_x[0] = (-0.5*t*t*t + t*t - 0.5*t);
   weights_x[1] = (1.5*t*t*t - 2.5*t*t + 1);
   weights_x[2] = (-1.5*t*t*t + 2*t*t + 0.5*t);
   weights_x[3] = (0.5*t*t*t - 0.5*t*t);
   
   double y_values[4];
   for (int j=0; j<4; j++){
      y_values[j] = weights_x[0]*components[j*4+0]+weights_x[1]*components[j*4+1]+weights_x[2]*components[j*4+2]+weights_x[3]*components[j*4+3];
   }
   
   double weights_y[4];
   t = fractional_y;
   weights_y[0] = (-0.5*t*t*t + t*t - 0.5*t);
   weights_y[1] = (1.5*t*t*t - 2.5*t*t + 1);
   weights_y[2] = (-1.5*t*t*t + 2*t*t + 0.5*t);
   weights_y[3] = (0.5*t*t*t - 0.5*t*t);
   
   double result = weights_y[0] * y_values[0]+weights_y[1] * y_values[1]+weights_y[2] * y_values[2]+weights_y[3] * y_values[3];
   return MathMax(0,MathMin(result,255));
}

void GetARGB(uint pixel, uchar &alpha, uchar &red, uchar &green, uchar &blue){
   alpha = (uchar)((pixel >> 24) & 0xFF);
   red = (uchar)((pixel >> 16) & 0xFF);
   green = (uchar)((pixel >> 8) & 0xFF);
   blue = (uchar)((pixel) & 0xFF);
}

We enter the machining area, where "DispalyImageOnChart()" is the core function for loading and scaling the image, like crafting a precision component. It loads the image from "ORIGINAL_IMAGE_RESOURSE" into "image_pixels[]" with "ResourceReadImage()", capturing "original_image_width" and "original_image_height". If this fails, it logs an error (“ERR: FAILED TO READ THE ORIGINAL DATA”) and returns false, like detecting a defective part.

The EA retrieves chart dimensions with "ChartGetInteger(CHART_WIDTH_IN_PIXELS)", "ChartGetInteger(CHART_HEIGHT_IN_PIXELS)", calculating aspect ratios ("image_aspect_ratio", "chart_aspect_ratio") to determine "scaled_image_width" and "scaled_image_height". If the image’s aspect ratio exceeds the chart’s, it scales width to "chart_pixel_width" and adjusts height; otherwise, it prioritizes height, like sizing a component to fit a chassis. If "LimitToOriginalSize"=true, it caps scaling at original dimensions ("MathMin()") and recalibrates to maintain aspect ratio, ensuring no over-stretching, like respecting a part’s design limits.

It logs scaling details with "PrintFormat()", like a diagnostic report (e.g., “Original: 200x200, Chart: 1200x800, Scaled: 800x533”). The "ScaleImage()" function applies bicubic interpolation, calling "BicubicInterpolate()" for each pixel, which uses a 4x4 pixel neighborhood ("neighborhood_pixels[16]") to compute smooth color transitions via "BicubicInterpolateComponent()". This separates ARGB components with "GetARGB()", interpolates each (alpha, red, green, blue), and recombines them, like precision machining for flawless quality. The scaled pixels are stored in "scaled_pixels[]" and copied to "image_pixels[]" with "ArrayCopy()".

A new resource ("scaled_resource_name") is created with "ResourceCreate()", using "COLOR_FORMAT_ARGB_NORMALIZE", like finalizing a machined part. If this fails, it logs an error. Positioning is set with "CenterImageDynamically" (centering via (chart_pixel_width - scaled_image_width)/2) or "AnchorCorner" (e.g., TOP_LEFT at "xOffsetFromCorner", "yOffsetFromCorner"=100), like mounting a component precisely. "CreateFullChartImage()" displays the image, completing the phase, like installing the part in the system. For example, a 200x200-pixel logo on a EURUSD H1 chart scales to 800x533 pixels, centered at (200,133), maintaining crisp quality.

Phase 3: Installing the Component—Displaying the Image

With the image scaled, we display it on the chart, like installing a precision component in a high-tech system.

void CreateFullChartImage(string object_name, string resource_name, int x_size, int y_size, int x_offset, int y_offset, bool is_background){
   if (ObjectFind(0,object_name) < 0){
      ObjectCreate(0,object_name,OBJ_BITMAP_LABEL,0,0,0);
   }
   ObjectSetString(0,object_name,OBJPROP_BMPFILE,resource_name);
   ObjectSetInteger(0,object_name,OBJPROP_XSIZE,x_size);
   ObjectSetInteger(0,object_name,OBJPROP_YSIZE,y_size);
   ObjectSetInteger(0,object_name,OBJPROP_XDISTANCE,x_offset);
   ObjectSetInteger(0,object_name,OBJPROP_YDISTANCE,y_offset);
   ObjectSetInteger(0,object_name,OBJPROP_BACK,is_background);
   ChartRedraw(0);
}

In the assembly area, "CreateFullChartImage()" installs the component, creating the "CHART_IMAGE_OBJECT_NAME" (“ChartImage”) object with "ObjectCreate(OBJ_BITMAP_LABEL)" if it doesn’t exist ("ObjectFind()"). It sets the scaled resource with "ObjectSetString(OBJPROP_BMPFILE)", sizes it to "x_size" and "y_size", positions it at "x_offset", "y_offset", and toggles background/foreground with "ImageInBackground" via "OBJPROP_BACK". "ChartRedraw()" refreshes the display, like activating the system.

For instance, on a GBPUSD chart, a 800x533-pixel logo is centered at (200,133) as a foreground overlay ("ImageInBackground"=false), enhancing the chart without obscuring candlesticks, like a perfectly integrated display module.

Phase 4: Maintaining Precision—Handling Chart Resizes

To ensure the component remains flawless, we update the image on chart resizes, like recalibrating a system for new specifications.

void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam){
   if (id == CHARTEVENT_CHART_CHANGE){
      if (!DispalyImageOnChart()){
         Print("ERR: Failed to update the image on the chart.");
      }
   }
}

In the maintenance bay, "OnChartEvent()" monitors system changes, like an engineer overseeing equipment performance. On a resize event ("id == CHARTEVENT_CHART_CHANGE"), it calls "DispalyImageOnChart()" to reload and rescale the image, ensuring it fits the new dimensions, like adjusting a display for a larger chassis. If it fails, it logs an error (“ERR: Failed to update the image”), maintaining system integrity. For example, resizing a EURUSD chart from 1200x800 to 1600x1000 pixels triggers a rescale to 1067x711, re-centered at (267,145), preserving visual quality.

Phase 5: Shutting Down the Workshop—Cleaning Up Resources

As our expedition concludes, we shut down the workshop, ensuring resources are cleared for the next project.

void OnDeinit(const int reason){
   ObjectDelete(0, CHART_IMAGE_OBJECT_NAME);
}

In the workshop’s cleanup area, "OnDeinit()" clears the "CHART_IMAGE_OBJECT_NAME" object with "ObjectDelete()", like dismantling a component from the system. However, it doesn’t free the scaled resource ("scaled_resource_name") or arrays ("image_pixels[]"), which could be added for completeness:

ResourceFree(scaled_resource_name);
ArrayFree(image_pixels);

For now, the minimal cleanup ensures a tidy chart, like archiving a project’s core elements, ready for the next engineering task.

Why This EA is a Precision Engineering Triumph (and Keeps You Engaged!)

The Image Scaling via Bicubic Interpolation EA is a technical triumph, delivering high-quality chart visuals with the precision of a master-crafted component. Its bicubic interpolation, aspect ratio preservation, and flexible positioning ("CenterImageDynamically", "AnchorCorner") ensure a seamless, professional workspace, with potential for enhancements like custom image paths or opacity. Picture a crisp logo on a EURUSD chart, scaled to 800x533 pixels, enhancing your analysis with branded elegance—pure engineering excellence! Beginners will value the clear functionality, while experts can refine its robust framework, making it an essential tool for traders seeking a visually superior chart environment.

Putting It All Together

To deploy this EA:

  1. Open MetaEditor in MetaTrader 5, like entering your engineering workshop.

  2. Copy the code, compile with F5, and verify no errors—no engineer wants a faulty component!

  3. Attach the EA to your chart and watch the logo appear, scaled and positioned with precision, like a perfectly installed display.

  4. Resize the chart to test dynamic scaling, and review logs (e.g., “ERR: FAILED TO READ THE ORIGINAL DATA”) for issues, like checking system diagnostics.

  5. Test on a demo account to ensure compatibility with trading EAs—your system deserves a trial run!

Conclusion

We’ve engineered a Bicubic Image Scaler that enhances MetaTrader 5 charts with high-quality visuals, scaled via bicubic interpolation with the precision of a master-crafted component. This MQL5 code is your engineering toolkit, brought to life with a seamless, professional narrative that flows with technical clarity, packed with precise explanations and vivid examples to fuel your trading ambition. Whether you’re a novice engineer or a seasoned market technician, this EA empowers you to craft a chart environment that stands out with excellence. Ready to elevate your workspace? Explore our video guide on the website for a detailed technical walkthrough. Now, build your trading legacy with precision! 🔧

Disclaimer: Trading is like engineering a high-tech system—complex and risky. Losses can exceed deposits. Test enhancements on a demo account before going live.

Disclaimer: The ideas and strategies presented in this resource are solely those of the author and are intended for informational and educational purposes only. They do not constitute financial advice, and past performance is not indicative of future results. All materials, including but not limited to text, images, files, and any downloadable content, are protected by copyright and intellectual property laws and are the exclusive property of Forex Algo-Trader or its licensors. Reproduction, distribution, modification, or commercial use of these materials without prior written consent from Forex Algo-Trader is strictly prohibited and may result in legal action. Users are advised to exercise extreme caution, perform thorough independent research, and consult with qualified financial professionals before implementing any trading strategies or decisions based on this resource, as trading in financial markets involves significant risk of loss.

Recent Comments

Go to discussion to Comment or View other Comments

No comments yet. Be the first to comment!