Creación de recursos dinámicos: ResourceCreate

Las directivas #resource incrustan recursos en el programa en la fase de compilación, por lo que pueden denominarse estáticas. Sin embargo, a menudo es necesario generar recursos (crear unos completamente nuevos o modificar los existentes) en la fase de ejecución del programa. Para estos propósitos, MQL5 ofrece la función ResourceCreate. Los recursos creados con ayuda de esta función se denominarán dinámicos.

La función tiene dos formas: la primera permite cargar imágenes y sonidos desde archivos, y la segunda está diseñada para crear imágenes de mapa de bits a partir de un array de píxeles preparado en memoria.

bool ResourceCreate(const string resource, const string filepath)

La función carga el recurso denominado resource desde un archivo ubicado en filepath. Si la ruta comienza con una barra invertida '\' (en cadenas constantes debe duplicarse: «\\path\\name.ext»), entonces el archivo se busca en esta ruta relativa a la carpeta MQL5 en el directorio de datos del terminal (por ejemplo, «\Files \CustomSounds\\Hello.wav» se refiere a MQL5/Files/CustomSounds/Hello.wav). Si no hay barra invertida, el recurso se busca a partir de la carpeta donde se encuentra el archivo ejecutable desde el que llamamos a la función.

La ruta puede apuntar a un recurso estático integrado en un programa MQL de terceros o actual. Por ejemplo, un determinado script es capaz de crear un recurso basado en una imagen del indicador BmpOwner.mq5 comentado en la sección sobre variables de recursos.

ResourceCreate("::MyImage""\\Indicators\\MQL5Book\\p7\\BmpOwner.ex5::search1.bmp");

El nombre del recurso en el parámetro resource puede contener un doble dos puntos inicial (aunque esto no es necesario, ya que, si no está presente, el prefijo «::» se añadirá al nombre automáticamente). Esto garantiza la unificación del uso de una línea para declarar un recurso en la llamada a ResourceCreate, así como para el acceso posterior al mismo (por ejemplo, al establecer la propiedad OBJPROP_BMPFILE).

Por supuesto, la declaración anterior para crear un recurso dinámico es redundante si solo queremos cargar un recurso de imagen de terceros en nuestro objeto en el gráfico, ya que es suficiente con asignar directamente la cadena «\Indicators\MQL5Book\p7\BmpOwner.ex5:» a la propiedad OBJPROP_BMPFILE: search1.bmp. Sin embargo, si necesita editar una imagen, un recurso dinámico es indispensable. A continuación, mostraremos un ejemplo en la sección Leer y modificar datos de recursos.

Los recursos dinámicos están disponibles públicamente desde otros programas MQL por su nombre completo, que incluye la ruta y el nombre del programa que creó el recurso. Por ejemplo, si la anterior llamada a ResourceCreate fue producida por el script MQL5/Scripts/MyExample.ex5, entonces otro programa MQL puede acceder al mismo recurso utilizando el enlace completo «\\Scripts\\MyExample.ex5::MyImage», y cualquier otro script en la misma carpeta puede acceder a la abreviatura «MyExample.ex5::MyImage» (aquí la ruta relativa es simplemente degenerada). Las reglas para escribir rutas completas (desde la carpeta raíz de MQL5) y relativas se dieron anteriormente.

La función ResourceCreate devuelve un indicador booleano de éxito (true) o error (false) como resultado de la ejecución. El código de error, como de costumbre, se puede encontrar en la variable _LastError. En concreto, es probable que reciba los siguientes errores:

  • ERR_RESOURCE_NAME_DUPLICATED (4015) - coincidencia de los nombres de los recursos dinámicos y estáticos
  • ERR_RESOURCE_NOT_FOUND (4016) - no se encuentra el recurso/archivo indicado en el parámetro filepath
  • ERR_RESOURCE_UNSUPPOTED_TYPE (4017) - tipo de recurso no admitido o tamaño superior a 2 GB
  • ERR_RESOURCE_NAME_IS_TOO_LONG (4018) - el nombre del recurso supera los 63 caracteres

Todo esto se aplica no sólo a la primera forma de la función, sino también a la segunda.

bool ResourceCreate(const string resource, const uint &data[], uint img_width, uint img_height, uint data_xoffset, uint data_yoffset, uint data_width, ENUM_COLOR_FORMAT color_format)

El parámetro resource sigue significando el nombre del nuevo recurso, y el contenido de la imagen viene dado por el resto de parámetros.

El array data puede ser unidimensional (data[]) o bidimensional (data[][]): pasa los puntos (píxeles) de la trama. Los parámetros img_width y img_height establecen las dimensiones de la imagen visualizada (en píxeles). Estos tamaños pueden ser inferiores al tamaño físico de la imagen en el array data, debido a lo cual se consigue el efecto de encuadre cuando solo se emite una parte de la imagen original. Los parámetros data_xoffset y data_yoffset determinan la coordenada de la esquina superior izquierda del «marco».

El parámetro data_width indica la anchura total de la imagen original (en el array data). Un valor de 0 implica que esta anchura es la misma que img_width. El parámetro data_width solo tiene sentido cuando se especifica un array unidimensional en el parámetro data, ya que para un array bidimensional se conocen sus dimensiones en ambas dimensiones (en este caso, el parámetro data_width se ignora y se asume igual a la segunda dimensión del array data[][]).

En el caso más común, cuando desee mostrar la imagen completa («tal cual»), utilice la siguiente sintaxis:

ResourceCreate(namedatawidthheight000, ...);

Por ejemplo, si el programa tiene un recurso estático descrito como un array bidimensional bitmap:

#resource "static.bmp" as bitmap data[][]

A continuación, la creación de un recurso dinámico basado en él puede realizarse de la siguiente manera:

ResourceCreate("dynamic"dataArrayRange(data1), ArrayRange(data0), 000, ...);

La creación de un recurso dinámico basado en uno estático es muy solicitada no solo cuando se requiere una edición directa, sino también para controlar cómo se procesan los colores al mostrar un recurso. Este modo se selecciona utilizando el último parámetro de la función: color_format. Utiliza la enumeración ENUM_COLOR_FORMAT.

Identificador

Descripción

COLOR_FORMAT_XRGB_NOALPHA

Se ignora el componente del canal alfa (transparencia)

COLOR_FORMAT_ARGB_RAW

El terminal no procesa los componentes de color

COLOR_FORMAT_ARGB_NORMALIZE

Los componentes de color son procesados por el terminal (véase más abajo)

En el modo COLOR_FORMAT_XRGB_NOALPHA, la imagen se muestra sin efectos: cada punto se muestra en un color sólido (es la forma más rápida de dibujar). Los otros dos modos muestran los píxeles teniendo en cuenta la transparencia en el byte alto de cada píxel, pero tienen efectos diferentes. En el caso de COLOR_FORMAT_ARGB_NORMALIZE, el terminal realiza las siguientes transformaciones de los componentes de color de cada punto al preparar el ráster en el momento de la llamada a ResourceCreate:

R = R * A / 255
G = G * A / 255
B = B * A / 255
A = A

Los recursos de imágenes estáticas en directivas #resource están conectadas con la ayuda de COLOR_FORMAT_ARGB_NORMALIZE.

En un recurso dinámico, el tamaño del array está limitado por el valor de INT_MAX bytes (2147483647, 2 Gb), que supera considerablemente el límite impuesto por el compilador al procesar la directiva estática #resource: el tamaño del archivo no puede superar los 128 Mb.

Si se llama a la segunda versión de la función para crear un recurso con el mismo nombre, pero cambiando otros parámetros (el contenido del array de píxeles, la anchura, la altura o el desplazamiento), entonces no se vuelve a crear el nuevo recurso, sino que simplemente se actualiza el existente. Solo el programa propietario del recurso (el programa que lo creó en primer lugar) puede modificar un recurso de esta forma.

Si, al crear recursos dinámicos a partir de distintas copias del programa que se ejecutan en distintos gráficos, necesita su propio recurso en cada copia, deberá añadir ChartID al nombre del recurso.

Para demostrar la creación dinámica de imágenes en varios esquemas de color, proponemos desmontar el script ARGBbitmap.mq5.

Se adjunta estáticamente la imagen «argb.bmp».

#resource "argb.bmp" as bitmap Data[][]

El usuario selecciona el método de formateo del color mediante el parámetro ColorFormat.

input ENUM_COLOR_FORMAT ColorFormat = COLOR_FORMAT_XRGB_NOALPHA;

El nombre del objeto en el que se mostrará la imagen y el nombre del recurso dinámico se describen mediante las variables BitmapObject y ResName.

const string BitmapObject = "BitmapObject";
const string ResName = "::image";

A continuación se muestra la función principal del script.

void OnStart()
{
   ResourceCreate(ResNameDataArrayRange(Data1), ArrayRange(Data0),
      000ColorFormat);
   
   ObjectCreate(0BitmapObjectOBJ_BITMAP_LABEL000);
   ObjectSetInteger(0BitmapObjectOBJPROP_XDISTANCE50);
   ObjectSetInteger(0BitmapObjectOBJPROP_YDISTANCE50);
   ObjectSetString(0BitmapObjectOBJPROP_BMPFILEResName);
   
   Comment("Press ESC to stop the demo");
   const ulong start = TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE);
   while(!IsStopped()  // waiting for the user's command to end the demo
   && TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE) == start)
   {
      Sleep(1000);
   }
   
   Comment("");
   ObjectDelete(0BitmapObject);
   ResourceFree(ResName);
}

El script crea un nuevo recurso en el modo de color especificado y lo asigna a la propiedad OBJPROP_BMPFILE de un objeto de tipo OBJ_BITMAP_LABEL. A continuación, el script espera a que el usuario detenga explícitamente el script o pulse Esc y luego borra el objeto (llamando a ObjectDelete) y el recurso utilizando la función ResourceFree. Tenga en cuenta que borrar un objeto no elimina automáticamente el recurso. Por eso necesitamos la función ResourceFree, de la que hablaremos en la sección siguiente.

Si no llamamos a ResourceFree, los recursos dinámicos permanecen en la memoria del terminal incluso después de que finalice el programa MQL, hasta que se cierre el terminal. Esto permite utilizarlos como repositorios o como medio de intercambio de información entre programas MQL.

Un recurso dinámico creado mediante la segunda forma de ResourceCreate no tiene por qué llevar ninguna imagen. El array data puede contener datos arbitrarios si no la utilizamos para el renderizado. En este caso, es importante establecer el esquema COLOR_FORMAT_XRGB_NOALPHA. En algún momento mostraremos un ejemplo de este tipo.

Mientras tanto, comprobemos cómo funciona el script ARGBbitmap.mq5.

La imagen anterior «argb.bmp» contiene información sobre la transparencia: la esquina superior izquierda tiene un fondo completamente transparente, y la transparencia se desvanece en diagonal hacia la esquina inferior derecha.

Las siguientes imágenes muestran los resultados de ejecutar el script en tres modos diferentes.

Salida de imagen en formato de color COLOR_FORMAT_XRGB_NOALPHA

Salida de imagen en formato de color COLOR_FORMAT_XRGB_NOALPHA

 

Salida de imagen en formato de color COLOR_FORMAT_ARGB_RAW

Salida de imagen en formato de color COLOR_FORMAT_ARGB_RAW

 

Salida de imagen en formato de color COLOR_FORMAT_ARGB_NORMALIZE

Salida de imagen en formato de color COLOR_FORMAT_ARGB_NORMALIZE