Программное формирование картинки в 1С:Предприятие 8.3.9 (ПотокВПамяти / ЗаписьДанных)

В статье представлен код программного формирования картинки (двоичные данные) без всяких внешних компонент, без формирования строки Base64, используя лишь встроенные механизмы 1С предприятие 8.3.9 (объекты ПотокВПамяти и ЗаписьДанных). Сам по себе код формирования картинки не несёт ничего нового. Всё описано в википедии. Захотелось попробовать новые методы, но никак не доходитили руки. В приложении обработка практически с этим же кодом.

Приступим к делу.

Будем формировать картинку в два этапа:
1. Сначала подготовим данные картинки, т.н. RGB матрицу. Именно здесь мы рисуем нашу будущую картинку путём определения цвета каждого пикселя. Т.е., если нужно нарисовать что-то своё, то меняем код именно здесь. 
2. А потом по этим данным уже сформируем двоичные данные.
Листинг функций представлен ниже. Плюс небольшие замечания.

Обработка создавалась любопытства ради. Применение каждый может определить для себя сам.

Итак, к делу:

Сначала вспомогательная функция формирования матрицы цветов пикселей. Собственно здесь мы описываем цвет каждого пикселя по его координатам.
Создаём двумерный массив высотой и шириной в пикселях с нашу картинку. Элементы массива — структура с RGB компонентами цвета. Не самый лучший вариант в плане производительности, но самый наглядный из тех, что я смог придумать.

&НаСервереБезКонтекста
Функция МатрицаЦветовRGB(Высота, Ширина)

// Устанавливаем размеры матрицы по которой сформируется картинка
ТекМатрицаЦветовRGB = Новый Массив(Высота, Ширина);

Для Индекс1 = 1 По Высота Цикл
Для Индекс2 = 1 По Ширина Цикл

// Далее идёт установка цвета пикселя в зависимости от координат.
// Именно этот код предполагается модифицировать
#Область Определение_цвета_пикселя

Если ((Cos(Индекс2/Ширина*5*3.14)/2+0.5)*0.6 + 0.4)*Высота > Индекс1 Тогда
ТекКрасный  = Цел(255*Индекс1/Высота);
ТекЗеленый  = 50;
ТекСиний  = 50;
Иначе
ТекКрасный = 255;
ТекЗеленый = 255;
ТекСиний = 255;
КонецЕсли;

#КонецОбласти

ТекМатрицаЦветовRGB[Индекс1-1][Индекс2-1] = Новый Цвет(ТекКрасный, ТекЗеленый, ТекСиний);

КонецЦикла;
КонецЦикла;

Возврат ТекМатрицаЦветовRGB

КонецФункции

По матрице формируем двоичные данные картинки. Большую часть кода занимает формирование шапки файла. Описание значений можно найти на википедии. Хотя там и написано довольно понятно, однако на создание рабочего кода у меня ушло несколько часов.

&НаСервереБезКонтекста
Функция СоздатьКартинку(МатрицаЦветовRGB)

Высота = МатрицаЦветовRGB.Количество();
Ширина = МатрицаЦветовRGB[0].Количество();

// Для простоты формирования возьемём 24 бита. По 8 бит на каждый канал. Альфаканал не используем.
ТекГлубинаЦвета = 3; // Измеряется в байтах.

// Каждая строка должна содержать количество байтов кратное 4.
БайтовДополнение = (4-ТекГлубинаЦвета*Ширина%4)%4;

РазмерФайла = ТекГлубинаЦвета*Ширина*Высота + Высота*БайтовДополнение;

///////////////////////////////////////////////////////////////////////////////

ПотокТело = Новый ПотокВПамяти();
ЗаписьДанных = Новый ЗаписьДанных(ПотокТело);

//BITMAPFILEHEADER

//bfType
ЗаписьДанных.ЗаписатьЦелое16(16973, ПорядокБайтов.BigEndian); // 0x424D big-endian = 0x4D42 little-endian. Признак формата. Всегда это значение.
//bfSize
ЗаписьДанных.ЗаписатьЦелое32(54 + РазмерФайла);
//bfReserved1
ЗаписьДанных.ЗаписатьЦелое16(0);
//bfReserved2
ЗаписьДанных.ЗаписатьЦелое16(0);
//bfOffBits
ЗаписьДанных.ЗаписатьЦелое32(54);

//BITMAPINFOHEADER // версия 3
// biSize
ЗаписьДанных.ЗаписатьЦелое32(40);
// biWidth
ЗаписьДанных.ЗаписатьЦелое32(Ширина);   // ширина изображения в пикселах
// biHeight
ЗаписьДанных.ЗаписатьЦелое32(Высота);   // высота изображения в пикселах
// biPlanes
ЗаписьДанных.ЗаписатьЦелое16(1);    // содержит единицу
// biBitCount
ЗаписьДанных.ЗаписатьЦелое16(ТекГлубинаЦвета*8); // количество бит на пиксел
// biCompression
ЗаписьДанных.ЗаписатьЦелое32(0);    // тип сжатия
// biSizeImage
ЗаписьДанных.ЗаписатьЦелое32(РазмерФайла);  // размер изображения в байтах
// biXPelsPerMeter
ЗаписьДанных.ЗаписатьЦелое32(0);    // горизонтальное разрешение в пикселах на метр
// biYPelsPerMeter
ЗаписьДанных.ЗаписатьЦелое32(0);     // вертикальное разрешение в пикселах на метр
// biClrUsed
ЗаписьДанных.ЗаписатьЦелое32(0);    //  количество используемых цветовых индексов в палитре
// biClrImportant
ЗаписьДанных.ЗаписатьЦелое32(0);    // количество индексов

///////////////////////////////////////////////////////////////////////////////

Для Выс = 1 По Высота Цикл // строки снизу вверх

Для Шир = 1 По Ширина Цикл

ЦветаПикселя = МатрицаЦветовRGB[Выс-1][Шир-1];
ЗаписьДанных.ЗаписатьБайт(ЦветаПикселя.Синий);
ЗаписьДанных.ЗаписатьБайт(ЦветаПикселя.Зеленый);
ЗаписьДанных.ЗаписатьБайт(ЦветаПикселя.Красный);

КонецЦикла;

// Забиваем нулями остаток строки. Так как размер строки должен быть кратен 4 байтам.
Для Доп = 1 По БайтовДополнение Цикл
ЗаписьДанных.ЗаписатьБайт(0);
КонецЦикла;

КонецЦикла;

ЗаписьДанных.Закрыть();

ДвоичныеДанныеТело = ПотокТело.ЗакрытьИПолучитьДвоичныеДанные();

Возврат ДвоичныеДанныеТело;

КонецФункции

 

Теперь вернёмся к вопросу об использовании. Почему-то в 1С у меня не хотели отображаться bmp-файлы. Даже, если я их создавал в паинте. Получилось обойти проблему с помощью встроенного метода Преобразовать, преобразовывая картинки в формат png:

ТекМатрицаЦветовRGB = МатрицаЦветовRGB(ВысотаКартинок, 200);
ТекКартинкаДвоичныеДанные = СоздатьКартинку(ТекМатрицаЦветовRGB);

ТекКартинка = Новый Картинка(ТекКартинкаДвоичныеДанные);
ТекКартинкаПНГ = ТекКартинка.Преобразовать(ФорматКартинки.PNG);

Здесь же на инфостарте был найден способ отображения картинок в табичной части / динамическом списке используя навигационные ссылки. Статью к сожалению, найти не могу. В кратце:
Создаём справочник КартинкиДляТаблицы с двумя реквизитами:
1. ДанныеКартинки — Тип: ХранилищеЗначения
2. АдресКартинки — Тип: Строка(0)

Формируем картинку, и записываем в элемент справочника:

 

// Создаём картинку
ТекКартинкаДвоичныеДанные = СоздатьКартинку(ТекМатрицаЦветовRGB);
ТекКартинка = Новый Картинка(ТекКартинкаДвоичныеДанные);

// Преобразуем в PNG
ТекКартинкаПНГ = ТекКартинка.Преобразовать(ФорматКартинки.PNG);

// Создаём элемент справочника
НовЭлемент = Справочники.КартинкиДляТаблицы.СоздатьЭлемент();

// Прописываем наименование, если нужно
НовЭлемент.Наименование = "График1";

// Присваиваем данные картинки. Формат PNG уже сжат, поэтому тратить ресурсы на излишнее сжатие не будем.
НовЭлемент.ДанныеКартинки = Новый ХранилищеЗначения(ТекКартинкаПНГ, Новый СжатиеДанных(0));

// Записываем, так как для ПолучитьНавигационнуюСсылку нужна ссылка.
НовЭлемент.Записать();

// Получаем навигационную ссылку на реквзит "ДанныеКартинки" у данного элемента справочника
// и записываем полученное значение
НовЭлемент.АдресКартинки = ПолучитьНавигационнуюСсылку(НовЭлемент.Ссылка, "ДанныеКартинки");

// После чего сохраняем элемент справочника
НовЭлемент.Записать();

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

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КартинкиДляТаблицы.Ссылка КАК Ссылка,
| КартинкиДляТаблицы.АдресКартинки КАК АдресКартинки
|ИЗ
| Справочник.КартинкиДляТаблицы КАК КартинкиДляТаблицы";
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
НовСтрока = Таблица.Добавить();
НовСтрока.Картинка = Выборка.АдресКартинки;
НовСтрока.Справочник = Выборка.Ссылка;
КонецЦикла;

Вуаля:

 

В файлах:
1. обработка с этим кодом
2. выгрузка конфигурации с этим справочником картинок и обработкой.

7 Comments

  1. Serj1C

    Круто!

    Тоже с 2013 года хочу свою обработку адаптировать под новые возможности 8.3

    https://infostart.ru/public/77713/

    Как долго генерируется картинка?

    Reply
  2. yku

    (1) Пример из статьи: 25*200 пикселей. Рисунок тоже из статьи — герерируется за 0.17-0.20 сек в среднем.

    Я твою кстати скачивал. И возможно часть кода из неё почерпнул.

    Тоже хочу сделать команды для рисования.

    Reply
  3. Darklight

    Волны в примере бегут? Если нет динамики — то это не так интересно

    Reply
  4. Поручик

    Пригодится на будущее

    Reply
  5. KAV2

    (2) И создать обертку над ними в JS Canvas )).

    Reply
  6. yku

    (3) конечно бегут. Это же bmp. И статья так и называется: «создаём анимацию» нет.

    Reply
  7. Жолтокнижниг

    Есть еще вариант формирования картинок — SVG, поддержка с 8.3.?

    Увы 1с не умеет SVG анимацию.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *