Приступим к делу.
Будем формировать картинку в два этапа:
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. выгрузка конфигурации с этим справочником картинок и обработкой.