Динамическое создание ресурсов: ResourceCreate

Директивы #resource встраивают ресурсы в программу на этапе компиляции и потому их можно назвать статическими. Однако часто возникает необходимость генерировать ресурсы (создавать полностью новые или модифицировать имеющиеся) на стадии выполнения программы. Для этих целей MQL5 предоставляет функцию ResourceCreate. Созданные с помощью неё ресурсы будем называть динамическими.

Функция имеет две формы: первая позволяет загружать из файлов картинки и звуки, вторая предназначена для создания растровых изображений на основе подготовленного в памяти массива пикселей.

bool ResourceCreate(const string resource, const string filepath)

Функция загружает ресурс под именем resource из файла, расположенного по пути filepath. Если путь начинается с обратной косой черты '\' (в константных строках его следует задваивать: "\\path\\name.ext"), то файл ищется по этому пути относительно папки MQL5 в каталоге данных терминала (например, "\\Files\\CustomSounds\\Hello.wav" ссылается на MQL5/Files/CustomSounds/Hello.wav). Если обратной косой черты нет, то ресурс ищется начиная с папки, в которой расположен исполняемый файл, из которого вызывается функция.

Путь может указывать на статический ресурс, "зашитый" в стороннюю или в текущую MQL-программу. Например, некий скрипт способен создать ресурс на основе картинки из индикатора BmpOwner.mq5, рассмотренного в разделе о Ресурсных переменных.

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

Имя ресурса в параметре resource может содержать начальное двойное двоеточие (хотя это не обязательно, т.к. если его нет, префикс "::" добавится к имени автоматически) — за счет этого обеспечивается унификация использования одной строки как для объявления ресурса в вызове ResourceCreate, так и последующее обращение к нему (например, при установке свойства OBJPROP_BMPFILE).

Разумеется, приведенный выше оператор по созданию динамического ресурса избыточен, если мы просто хотим загрузить сторонний ресурс-картинку в свой объект на графике, поскольку достаточно напрямую присвоить свойству OBJPROP_BMPFILE строку "\\Indicators\\MQL5Book\\p7\\BmpOwner.ex5::search1.bmp". Однако если требуется отредактировать изображение, динамический ресурс незаменим. Далее мы покажем пример в разделе Чтение и модификация данных ресурса.

Динамические ресурсы являются публично доступными из других MQL-программ по полному имени, которое включает путь и название создавшей ресурс программы. Например, если предыдущий вызов ResourceCreate делал скрипт MQL5/Scripts/MyExample.ex5, то другая MQL-программа может обратиться к тому же ресурсу по полной ссылке "\\Scripts\\MyExample.ex5::MyImage", а любой другой скрипт в той же папке — используя краткую запись "MyExample.ex5::MyImage" (здесь относительный путь просто вырожден). Правила записи полных (от корневой папки MQL5) и относительных путей приводились выше.

Функция ResourceCreate возвращает логический признак успеха (true) или ошибки (false) в результате выполнения. Код ошибки, как обычно, можно узнать в переменной _LastError. В частности, вероятно получение следующих ошибок:

  • ERR_RESOURCE_NAME_DUPLICATED (4015) — совпадение имени динамического и статического ресурсов;
  • ERR_RESOURCE_NOT_FOUND (4016) — заданный ресурс/файл из параметра filepath не найден;
  • ERR_RESOURCE_UNSUPPOTED_TYPE (4017) — неподдерживаемый тип ресурса или размер более 2 Gb;
  • ERR_RESOURCE_NAME_IS_TOO_LONG (4018) — имя ресурса превышает 63 символа.

Все это касается не только первой формы функции, но и второй.

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)

Параметр resource по-прежнему означает имя нового ресурса, а содержимое изображения задается остальными параметрами.

Массив data может быть одномерным (data[]) или двумерным (data[][]): в нем передаются точки (пиксели) растра. Параметры img_width и img_height устанавливают размеры отображаемого изображения (в пикселях). Эти размеры могут быть меньше физического размера картинки в массиве data, за счет чего достигается эффект кадрирования — вывода лишь части исходной картинки. Параметры data_xoffset и data_yoffset как раз определяют координату левого верхнего угла "кадра".

Параметр data_width означает полную ширину исходного изображения (в массиве data). Значение 0 подразумевает, что эта ширина совпадает с img_width. Параметр data_width имеет смысл только при указании одномерного массива в параметре data, поскольку для двумерного массива известны его размеры по обеим размерностям (при этом параметр data_width игнорируется и принимается равным второй размерности массива data[][]).

В наиболее распространенном случае, когда требуется вывести изображение полностью ("как есть"), применяйте синтаксис:

ResourceCreate(namedatawidthheight000, ...);

Например, если в программе имеется статический ресурс, описанный как двумерный массив bitmap:

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

то создание на его основе динамического ресурса возможно таким образом:

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

Создание динамического ресурса на основе статического востребовано не только при необходимости прямого редактирования, но и для управления способом обработки цветов при отображении ресурса. Этот режим выбирается с помощью последнего параметра функции — color_format. Для него применяется перечисление ENUM_COLOR_FORMAT.

Идентификатор

Описание

COLOR_FORMAT_XRGB_NOALPHA

Компонента альфа-канала (прозрачность) игнорируется

COLOR_FORMAT_ARGB_RAW

Компоненты цвета не обрабатываются терминалом

COLOR_FORMAT_ARGB_NORMALIZE

Компоненты цвета обрабатываются терминалом (см. ниже)

В режиме COLOR_FORMAT_XRGB_NOALPHA изображение выводится без эффектов: каждая точка отображается сплошным цветом (это наиболее быстрый способ отрисовки). Два других режима отображают пиксели с учетом прозрачности в старшем байте каждой точки, но имеют разный эффект. В случае COLOR_FORMAT_ARGB_NORMALIZE терминал выполняет следующие преобразования цветовых компонент каждой точки при подготовке растра в момент вызова ResourceCreate:

R = R * A / 255

G = G * A / 255

B = B * A / 255

A = A

Статические ресурсы-картинки в директивах #resource подключаются именно с помощью COLOR_FORMAT_ARGB_NORMALIZE.

В динамическом ресурсе размер массива ограничен значением INT_MAX байт (2147483647, 2 Gb), что существенно превышает предел, налагаемый компилятором при обработке статической директивы #resource: напомним, там размер файла не может превышать 128 Mb.

Если второй вариант функции вызывается для создания ресурса с одним и тем же именем, но с меняющимися другими параметрами (содержимым массива пикселей, шириной, высотой или сдвигом), то новый ресурс не пересоздается, а просто обновляется существующий. Изменять ресурс таким образом может только программа-хозяин ресурса (создавшая его изначально).

Если при создании динамических ресурсов из разных копий программы, выполняющихся на разных графиках, требуется собственный ресурс в каждой копии, следует добавлять ChartID в имя ресурса.

Для демонстрации динамического создания изображений в различных цветовых схемах предлагаем разобрать скрипт ARGBbitmap.mq5.

К нему статически подключено изображение "argb.bmp".

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

Способ форматирования цвета пользователь выбирает параметром ColorFormat.

input ENUM_COLOR_FORMAT ColorFormat = COLOR_FORMAT_XRGB_NOALPHA;

Имена объекта, в котором будет отображаться изображение, и динамического ресурса описаны переменными BitmapObject и ResName.

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

А вот и главная функция скрипта.

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()  // ждем команды пользователя на завершение демо
   && TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE) == start)
   {
      Sleep(1000);
   }
   
   Comment("");
   ObjectDelete(0BitmapObject);
   ResourceFree(ResName);
}

Скрипт создает новый ресурс в заданном цветовом режиме и назначает его свойству OBJPROP_BMPFILE объекта типа OBJ_BITMAP_LABEL. Далее скрипт ожидает явной остановки скрипта пользователем или нажатия клавиши Esc, после чего удаляет объект (вызвав ObjectDelete) и ресурс с помощью функции ResourceFree. Обратите внимание, что удаление объекта не означает автоматическое удаление ресурса. Именно поэтому нужна функция ResourceFree, о которой мы поговорим в следующем разделе.

Если не вызвать ResourceFree, динамические ресурсы остаются в памяти терминала даже после завершения работы MQL-программы — вплоть до закрытия терминала. Это позволяет использовать их как хранилища или средство для обмена информацией между MQL-программами.

Здесь уместно отметить, что динамический ресурс, созданный с помощью второй формы ResourceCreate, не обязан нести в себе изображение. Массив data может содержать произвольные данные, если мы не используем их для визуализации. При этом важно задавать схему COLOR_FORMAT_XRGB_NOALPHA. Такой пример мы еще покажем.

А пока проверим, как работает скрипт ARGBbitmap.mq5.

Вышеупомянутая картинка "argb.bmp" содержит информацию о прозрачности: верхний левый угол имеет совершенно прозрачный фон, а по диагонали по направлению к правому нижнем углу прозрачность постепенно исчезает.

На следующих изображениях показаны результаты запуска скрипта в трех разных режимах.

Вывод изображения в цветовом формате COLOR_FORMAT_XRGB_NOALPHA

Вывод изображения в цветовом формате COLOR_FORMAT_XRGB_NOALPHA

 

Вывод изображения в цветовом формате COLOR_FORMAT_ARGB_RAW

Вывод изображения в цветовом формате COLOR_FORMAT_ARGB_RAW

 

Вывод изображения в цветовом формате COLOR_FORMAT_ARGB_NORMALIZE

Вывод изображения в цветовом формате COLOR_FORMAT_ARGB_NORMALIZE