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

Публикация № 1061803

Разработка - Универсальные функции

картинка bmp программно

48
В статье представлен код программного формирования картинки (двоичные данные) без всяких внешних компонент, без формирования строки 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. выгрузка конфигурации с этим справочником картинок и обработкой.

48

Скачать файлы

Наименование Файл Версия Размер
Формирование картинки.epf
.epf 9,37Kb
17.05.19
3
.epf 9,37Kb 3 Скачать
СправочникДляКартинок_ФормированиеКартинок.cf
.cf 19,16Kb
17.05.19
2
.cf 19,16Kb 2 Скачать

См. также

Специальные предложения

Комментарии
Избранное Подписка Сортировка: Древо
1. Serj1C 475 17.05.19 11:39 Сейчас в теме
Круто!
Тоже с 2013 года хочу свою обработку адаптировать под новые возможности 8.3
https://infostart.ru/public/77713/

Как долго генерируется картинка?
2. yku 324 17.05.19 12:56 Сейчас в теме
(1) Пример из статьи: 25*200 пикселей. Рисунок тоже из статьи - герерируется за 0.17-0.20 сек в среднем.

Я твою кстати скачивал. И возможно часть кода из неё почерпнул.
Тоже хочу сделать команды для рисования.
5. KAV2 85 17.05.19 18:02 Сейчас в теме
(2) И создать обертку над ними в JS Canvas )).
3. Darklight 19 17.05.19 15:25 Сейчас в теме
Волны в примере бегут? Если нет динамики - то это не так интересно
6. yku 324 17.05.19 18:48 Сейчас в теме
(3) конечно бегут. Это же bmp. И статья так и называется: "создаём анимацию" нет.
CyberCerber; Itilive.ru; +2 Ответить
4. Поручик 4315 17.05.19 15:35 Сейчас в теме
7. Жолтокнижниг 247 09.06.19 23:54 Сейчас в теме
Есть еще вариант формирования картинок - SVG, поддержка с 8.3.?

Увы 1с не умеет SVG анимацию.
Оставьте свое сообщение