Добавляем текст на изображения в dlib

Когда я начал использовать dlib, то столкнулся с необходимостью вставки в изображение некоторого текста. Оказалось, что встроенной в dlib процедуры, которая рисует текст на картинке нет, а она была бы полезна в некоторых практических приложениях: отрисовке графиков функций с подписями осей, визуализации текстовой информации, подготовке диаграмм и иной инфографики. Я думал, с течением времени в dlib появится нечто вроде drawText или drawString, но постепенно пришел к выводу, что реализация такого примитива выходит за рамки самой библиотеки и сопряжена с немалыми трудностями, особенно, если учесть, что существует огромное количество шрифтов и сложности с их рендерингом.

Казалось бы, о добавлении каких-либо надписей с помощью dlib можно забыть, но я пошел обходным путём…

Примерно такая же проблема встретилась на моем пути, когда я работал с платой Sipeed MAIX Bit и миниатюрным OLED-экраном (0,91 дюйм диагональ). Дело в том, что пришлось, используя MicroPython и куски разных (заброшенных) решений создать драйвер для OLED-дисплея. Поначалу, все шло хорошо: простые фигуры рисовались, графики строились, но вот со строками все было плохо, ведь отрисовка символов гораздо сложнее, чем фигур, поскольку символы представляют собой комбинации множества различных геометрических форм.

Решение пришло в голову неожиданно: с миниатюрными дисплеями на базе OLED уже давно работают самодельщики и DIY’щики, поэтому мне сразу стоило бы ознакомиться с их достижениями, что и было сделано. У одного неизвестного гения нашлась фундаментальная идея: по сути шрифт — это некоторые блоки значений, в которых рисунок представлен в виде прямоугольной матрицы 7 на 5 точек; единицы в значениях соответствуют точкам, которые окрашиваются в цвет тона, а нули — в цвет фона…

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

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

А теперь все по порядку…

Для начала опишем массив, который будет содержать все доступные символы:

Длинный код под спойлером

#!/usr/bin/env dub 
/+ dub.sdl: 
	name "exp" 
	dependency "dlib" 
	version="~>0.17.0" 
+/ 

enum ubyte[] font = [ 	
	0x00, 0x00, 0x00, 0x00, 0x00, 
	0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 
	0x3E, 0x6B, 0x4F, 0x6B, 0x3E, 
	0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 
	0x18, 0x3C, 0x7E, 0x3C, 0x18, 
	0x1C, 0x57, 0x7D, 0x57, 0x1C, 
	0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 
	0x00, 0x18, 0x3C, 0x18, 0x00, 
	0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 
	0x00, 0x18, 0x24, 0x18, 0x00, 
	0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 
	0x30, 0x48, 0x3A, 0x06, 0x0E, 
	0x26, 0x29, 0x79, 0x29, 0x26, 
	0x40, 0x7F, 0x05, 0x05, 0x07, 
	0x40, 0x7F, 0x05, 0x25, 0x3F, 
	0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 
	0x7F, 0x3E, 0x1C, 0x1C, 0x08, 
	0x08, 0x1C, 0x1C, 0x3E, 0x7F, 
	0x14, 0x22, 0x7F, 0x22, 0x14, 
	0x5F, 0x5F, 0x00, 0x5F, 0x5F, 
	0x06, 0x09, 0x7F, 0x01, 0x7F, 
	0x00, 0x66, 0x89, 0x95, 0x6A, 
	0x60, 0x60, 0x60, 0x60, 0x60, 
	0x94, 0xA2, 0xFF, 0xA2, 0x94, 
	0x08, 0x04, 0x7E, 0x04, 0x08, 
	0x10, 0x20, 0x7E, 0x20, 0x10, 
	0x08, 0x08, 0x2A, 0x1C, 0x08, 
	0x08, 0x1C, 0x2A, 0x08, 0x08, 
	0x1E, 0x10, 0x10, 0x10, 0x10, 
	0x0C, 0x1E, 0x0C, 0x1E, 0x0C, 
	0x30, 0x38, 0x3E, 0x38, 0x30, 
	0x06, 0x0E, 0x3E, 0x0E, 0x06, 
	0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x5F, 0x00, 0x00, 
	0x00, 0x07, 0x00, 0x07, 0x00, 
	0x14, 0x7F, 0x14, 0x7F, 0x14, 
	0x24, 0x2A, 0x7F, 0x2A, 0x12, 
	0x23, 0x13, 0x08, 0x64, 0x62, 
	0x36, 0x49, 0x56, 0x20, 0x50, 
	0x00, 0x08, 0x07, 0x03, 0x00, 
	0x00, 0x1C, 0x22, 0x41, 0x00, 
	0x00, 0x41, 0x22, 0x1C, 0x00, 
	0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 
	0x08, 0x08, 0x3E, 0x08, 0x08, 
	0x00, 0x80, 0x70, 0x30, 0x00, 
	0x08, 0x08, 0x08, 0x08, 0x08, 
	0x00, 0x00, 0x60, 0x60, 0x00, 
	0x20, 0x10, 0x08, 0x04, 0x02, 
	0x3E, 0x51, 0x49, 0x45, 0x3E, 
	0x00, 0x42, 0x7F, 0x40, 0x00, 
	0x72, 0x49, 0x49, 0x49, 0x46, 
	0x21, 0x41, 0x49, 0x4D, 0x33, 
	0x18, 0x14, 0x12, 0x7F, 0x10, 
	0x27, 0x45, 0x45, 0x45, 0x39, 
	0x3C, 0x4A, 0x49, 0x49, 0x31, 
	0x41, 0x21, 0x11, 0x09, 0x07, 
	0x36, 0x49, 0x49, 0x49, 0x36, 
	0x46, 0x49, 0x49, 0x29, 0x1E, 
	0x00, 0x00, 0x14, 0x00, 0x00, 
	0x00, 0x40, 0x34, 0x00, 0x00, 
	0x00, 0x08, 0x14, 0x22, 0x41, 
	0x14, 0x14, 0x14, 0x14, 0x14, 
	0x00, 0x41, 0x22, 0x14, 0x08, 
	0x02, 0x01, 0x59, 0x09, 0x06, 
	0x3E, 0x41, 0x5D, 0x59, 0x4E, 
	0x7C, 0x12, 0x11, 0x12, 0x7C, 
	0x7F, 0x49, 0x49, 0x49, 0x36, 
	0x3E, 0x41, 0x41, 0x41, 0x22, 
	0x7F, 0x41, 0x41, 0x41, 0x3E, 
	0x7F, 0x49, 0x49, 0x49, 0x41, 
	0x7F, 0x09, 0x09, 0x09, 0x01, 
	0x3E, 0x41, 0x41, 0x51, 0x73, 
	0x7F, 0x08, 0x08, 0x08, 0x7F, 
	0x00, 0x41, 0x7F, 0x41, 0x00, 
	0x20, 0x40, 0x41, 0x3F, 0x01, 
	0x7F, 0x08, 0x14, 0x22, 0x41, 
	0x7F, 0x40, 0x40, 0x40, 0x40, 
	0x7F, 0x02, 0x1C, 0x02, 0x7F, 
	0x7F, 0x04, 0x08, 0x10, 0x7F, 
	0x3E, 0x41, 0x41, 0x41, 0x3E, 
	0x7F, 0x09, 0x09, 0x09, 0x06, 
	0x3E, 0x41, 0x51, 0x21, 0x5E, 
	0x7F, 0x09, 0x19, 0x29, 0x46, 
	0x26, 0x49, 0x49, 0x49, 0x32, 
	0x03, 0x01, 0x7F, 0x01, 0x03, 
	0x3F, 0x40, 0x40, 0x40, 0x3F, 
	0x1F, 0x20, 0x40, 0x20, 0x1F, 
	0x3F, 0x40, 0x38, 0x40, 0x3F, 
	0x63, 0x14, 0x08, 0x14, 0x63, 
	0x03, 0x04, 0x78, 0x04, 0x03, 
	0x61, 0x59, 0x49, 0x4D, 0x43, 
	0x00, 0x7F, 0x41, 0x41, 0x41, 
	0x02, 0x04, 0x08, 0x10, 0x20, 
	0x00, 0x41, 0x41, 0x41, 0x7F, 
	0x04, 0x02, 0x01, 0x02, 0x04, 
	0x40, 0x40, 0x40, 0x40, 0x40, 
	0x00, 0x03, 0x07, 0x08, 0x00, 
	0x20, 0x54, 0x54, 0x78, 0x40, 
	0x7F, 0x28, 0x44, 0x44, 0x38, 
	0x38, 0x44, 0x44, 0x44, 0x28, 
	0x38, 0x44, 0x44, 0x28, 0x7F, 
	0x38, 0x54, 0x54, 0x54, 0x18, 
	0x00, 0x08, 0x7E, 0x09, 0x02, 
	0x18, 0xA4, 0xA4, 0x9C, 0x78, 
	0x7F, 0x08, 0x04, 0x04, 0x78, 
	0x00, 0x44, 0x7D, 0x40, 0x00, 
	0x20, 0x40, 0x40, 0x3D, 0x00, 
	0x7F, 0x10, 0x28, 0x44, 0x00, 
	0x00, 0x41, 0x7F, 0x40, 0x00, 
	0x7C, 0x04, 0x78, 0x04, 0x78, 
	0x7C, 0x08, 0x04, 0x04, 0x78, 
	0x38, 0x44, 0x44, 0x44, 0x38, 
	0xFC, 0x18, 0x24, 0x24, 0x18, 
	0x18, 0x24, 0x24, 0x18, 0xFC, 
	0x7C, 0x08, 0x04, 0x04, 0x08, 
	0x48, 0x54, 0x54, 0x54, 0x24, 
	0x04, 0x04, 0x3F, 0x44, 0x24, 
	0x3C, 0x40, 0x40, 0x20, 0x7C, 
	0x1C, 0x20, 0x40, 0x20, 0x1C, 
	0x3C, 0x40, 0x30, 0x40, 0x3C, 
	0x44, 0x28, 0x10, 0x28, 0x44, 
	0x4C, 0x90, 0x90, 0x90, 0x7C, 
	0x44, 0x64, 0x54, 0x4C, 0x44, 
	0x00, 0x08, 0x36, 0x41, 0x00, 
	0x00, 0x00, 0x77, 0x00, 0x00, 
	0x00, 0x41, 0x36, 0x08, 0x00, 
	0x02, 0x01, 0x02, 0x04, 0x02, 
	0x3C, 0x26, 0x23, 0x26, 0x3C, 
	0x1E, 0xA1, 0xA1, 0x61, 0x12, 
	0x3A, 0x40, 0x40, 0x20, 0x7A, 
	0x38, 0x54, 0x54, 0x55, 0x59, 
	0x21, 0x55, 0x55, 0x79, 0x41, 
	0x22, 0x54, 0x54, 0x78, 0x42, 
	0x21, 0x55, 0x54, 0x78, 0x40, 
	0x20, 0x54, 0x55, 0x79, 0x40, 
	0x0C, 0x1E, 0x52, 0x72, 0x12, 
	0x39, 0x55, 0x55, 0x55, 0x59, 
	0x39, 0x54, 0x54, 0x54, 0x59, 
	0x39, 0x55, 0x54, 0x54, 0x58, 
	0x00, 0x00, 0x45, 0x7C, 0x41, 
	0x00, 0x02, 0x45, 0x7D, 0x42, 
	0x00, 0x01, 0x45, 0x7C, 0x40, 
	0x7D, 0x12, 0x11, 0x12, 0x7D, 
	0xF0, 0x28, 0x25, 0x28, 0xF0, 
	0x7C, 0x54, 0x55, 0x45, 0x00, 
	0x20, 0x54, 0x54, 0x7C, 0x54, 
	0x7C, 0x0A, 0x09, 0x7F, 0x49, 
	0x32, 0x49, 0x49, 0x49, 0x32, 
	0x3A, 0x44, 0x44, 0x44, 0x3A, 
	0x32, 0x4A, 0x48, 0x48, 0x30, 
	0x3A, 0x41, 0x41, 0x21, 0x7A, 
	0x3A, 0x42, 0x40, 0x20, 0x78, 
	0x00, 0x9D, 0xA0, 0xA0, 0x7D, 
	0x3D, 0x42, 0x42, 0x42, 0x3D, 
	0x3D, 0x40, 0x40, 0x40, 0x3D, 
	0x3C, 0x24, 0xFF, 0x24, 0x24, 
	0x48, 0x7E, 0x49, 0x43, 0x66, 
	0x2B, 0x2F, 0xFC, 0x2F, 0x2B, 
	0xFF, 0x09, 0x29, 0xF6, 0x20, 
	0xC0, 0x88, 0x7E, 0x09, 0x03, 
	0x20, 0x54, 0x54, 0x79, 0x41, 
	0x00, 0x00, 0x44, 0x7D, 0x41, 
	0x30, 0x48, 0x48, 0x4A, 0x32, 
	0x38, 0x40, 0x40, 0x22, 0x7A, 
	0x00, 0x7A, 0x0A, 0x0A, 0x72, 
	0x7D, 0x0D, 0x19, 0x31, 0x7D, 
	0x26, 0x29, 0x29, 0x2F, 0x28, 
	0x26, 0x29, 0x29, 0x29, 0x26, 
	0x30, 0x48, 0x4D, 0x40, 0x20, 
	0x38, 0x08, 0x08, 0x08, 0x08, 
	0x08, 0x08, 0x08, 0x08, 0x38, 
	0x2F, 0x10, 0xC8, 0xAC, 0xBA, 
	0x2F, 0x10, 0x28, 0x34, 0xFA, 
	0x00, 0x00, 0x7B, 0x00, 0x00, 
	0x08, 0x14, 0x2A, 0x14, 0x22, 
	0x22, 0x14, 0x2A, 0x14, 0x08, 
	0x55, 0x00, 0x55, 0x00, 0x55, 
	0xAA, 0x55, 0xAA, 0x55, 0xAA, 
	0xFF, 0x55, 0xFF, 0x55, 0xFF, 
	0x00, 0x00, 0x00, 0xFF, 0x00, 
	0x10, 0x10, 0x10, 0xFF, 0x00, 
	0x14, 0x14, 0x14, 0xFF, 0x00, 
	0x10, 0x10, 0xFF, 0x00, 0xFF, 
	0x10, 0x10, 0xF0, 0x10, 0xF0, 
	0x14, 0x14, 0x14, 0xFC, 0x00, 
	0x14, 0x14, 0xF7, 0x00, 0xFF, 
	0x00, 0x00, 0xFF, 0x00, 0xFF, 
	0x14, 0x14, 0xF4, 0x04, 0xFC, 
	0x14, 0x14, 0x17, 0x10, 0x1F, 
	0x10, 0x10, 0x1F, 0x10, 0x1F, 
	0x14, 0x14, 0x14, 0x1F, 0x00, 
	0x10, 0x10, 0x10, 0xF0, 0x00, 
	0x00, 0x00, 0x00, 0x1F, 0x10, 
	0x10, 0x10, 0x10, 0x1F, 0x10, 
	0x10, 0x10, 0x10, 0xF0, 0x10, 
	0x00, 0x00, 0x00, 0xFF, 0x10, 
	0x10, 0x10, 0x10, 0x10, 0x10, 
	0x10, 0x10, 0x10, 0xFF, 0x10, 
	0x00, 0x00, 0x00, 0xFF, 0x14, 
	0x00, 0x00, 0xFF, 0x00, 0xFF, 
	0x00, 0x00, 0x1F, 0x10, 0x17, 
	0x00, 0x00, 0xFC, 0x04, 0xF4, 
	0x14, 0x14, 0x17, 0x10, 0x17, 
	0x14, 0x14, 0xF4, 0x04, 0xF4, 
	0x00, 0x00, 0xFF, 0x00, 0xF7, 
	0x14, 0x14, 0x14, 0x14, 0x14, 
	0x14, 0x14, 0xF7, 0x00, 0xF7, 
	0x14, 0x14, 0x14, 0x17, 0x14, 
	0x10, 0x10, 0x1F, 0x10, 0x1F, 
	0x14, 0x14, 0x14, 0xF4, 0x14, 
	0x10, 0x10, 0xF0, 0x10, 0xF0, 
	0x00, 0x00, 0x1F, 0x10, 0x1F, 
	0x00, 0x00, 0x00, 0x1F, 0x14, 
	0x00, 0x00, 0x00, 0xFC, 0x14, 
	0x00, 0x00, 0xF0, 0x10, 0xF0, 
	0x10, 0x10, 0xFF, 0x10, 0xFF, 
	0x14, 0x14, 0x14, 0xFF, 0x14, 
	0x10, 0x10, 0x10, 0x1F, 0x00, 
	0x00, 0x00, 0x00, 0xF0, 0x10, 
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
	0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 
	0xFF, 0xFF, 0xFF, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xFF, 0xFF, 
	0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 
	0x38, 0x44, 0x44, 0x38, 0x44, 
	0xFC, 0x4A, 0x4A, 0x4A, 0x34, 
	0x7E, 0x02, 0x02, 0x06, 0x06, 
	0x02, 0x7E, 0x02, 0x7E, 0x02, 
	0x63, 0x55, 0x49, 0x41, 0x63, 
	0x38, 0x44, 0x44, 0x3C, 0x04, 
	0x40, 0x7E, 0x20, 0x1E, 0x20, 
	0x06, 0x02, 0x7E, 0x02, 0x02, 
	0x99, 0xA5, 0xE7, 0xA5, 0x99, 
	0x1C, 0x2A, 0x49, 0x2A, 0x1C, 
	0x4C, 0x72, 0x01, 0x72, 0x4C, 
	0x30, 0x4A, 0x4D, 0x4D, 0x30, 
	0x30, 0x48, 0x78, 0x48, 0x30, 
	0xBC, 0x62, 0x5A, 0x46, 0x3D, 
	0x3E, 0x49, 0x49, 0x49, 0x00, 
	0x7E, 0x01, 0x01, 0x01, 0x7E, 
	0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 
	0x44, 0x44, 0x5F, 0x44, 0x44, 
	0x40, 0x51, 0x4A, 0x44, 0x40, 
	0x40, 0x44, 0x4A, 0x51, 0x40, 
	0x00, 0x00, 0xFF, 0x01, 0x03, 
	0xE0, 0x80, 0xFF, 0x00, 0x00, 
	0x08, 0x08, 0x6B, 0x6B, 0x08, 
	0x36, 0x12, 0x36, 0x24, 0x36, 
	0x06, 0x0F, 0x09, 0x0F, 0x06, 
	0x00, 0x00, 0x18, 0x18, 0x00, 
	0x00, 0x00, 0x10, 0x10, 0x00, 
	0x30, 0x40, 0xFF, 0x01, 0x01, 
	0x00, 0x1F, 0x01, 0x01, 0x1E, 
	0x00, 0x19, 0x1D, 0x17, 0x12, 
	0x00, 0x3C, 0x3C, 0x3C, 0x3C ];

Для того, чтобы отобразить строку, мы воспользуемся одной особенностью данного массива, а именно блочной структурой (т.е. один символ шрифта — это блок из пяти байтов). Также, пригодится еще и тот факт, что в массиве шрифта все символы идут практически в том же порядке, что и в таблице ASCII, что позволяет использовать код символа в качестве индекса для блока (соответственно, индексы для отдельного символа будут умножаться на на 5, так как одному символу соответствует порция из пяти значений). Таким образом, алгоритм отрисовки отдельного символа из имеющегося блока массива сводиться к выделению нужного блока из пяти значений и последующей отрисовке блока как битовой карты.

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

Это все реализуется посредством простого применения вполне стандартных операций работы с массивами и побитовых операций (обратите внимание на вложенную функцию drawChar):

import dlib.image;

// напечатать строку (!) в рисунок
auto drawString(SuperImage superImage, int x, int y, string s, Color4f foreground = Color4f(1.0f, 1.0f, 1.0f), Color4f background = Color4f(0.0f, 0.0f, 0.0f))
{
	enum SCALE_FACTOR = 1.0;
	auto drawChar(SuperImage superImage, int x, int y, char c)
	{
		ubyte index = cast(ubyte) c;
		ubyte[] slice = font[(index * 5)..(index * 5 + 5)];
	
		foreach (i, e; slice)
		{
			enum ubyte mask = 0x01;
	
			foreach (j; 0..8)
			{
				auto t = e & (mask << j);
				superImage[cast(int) (SCALE_FACTOR * (x + i)), cast(int) (SCALE_FACTOR * (y + j))] = t ? foreground : background;
			}
		}
	}
	
	foreach (i, e; s)
	{
		drawChar(superImage, x + 6 * cast(int) i, y, e);
	}
}

В итоге, получаем процедуру, которая принимает на входе координаты x и y для начальной точки шрифта (т.е. для точки с которой начинается вся отрисовка текста, так сказать начальные координаты) и некоторый текст, в котором используются латинские буквы, цифры и знаки препинания. К сожалению, русский текст с данным массивом шрифта вывести нельзя, поскольку символов для русского алфавита в массиве нет. Кроме того, надписи на русском языке вывести не получится еще и по другой причине: дело все в конверсии текста, а именно, конкретного символа. Проблема заключается в том, что в нашем примере используется весьма жесткое и специфическое кодирование под конкретные данные (массив со шрифтом не просто так имеет такую удобную «структуру»), в частности, числовой код символа совпадает с индексом соответствующего блока в шрифте…

Также, в коде функции присутствует скрытая переменная настройки SCALE_FACTOR, которая позволяет сделать «масштабирование» шрифта под другой размер, и вы это также можете использовать.

Перейдем к испытаниям, для этого создадим функцию testFont, которая отобразит все символы нашего шрифта и будет самостоятельной вариацией процедуры drawString (практически оригинальный код до введения SCALE_FACTOR):

И просто создадим изображение с некоторым текстом, после основного тестирования всех символов шрифта:

auto testFont(SuperImage superImage, int x, int y, Color4f foreground = Color4f(1.0f, 1.0f, 1.0f), Color4f background = Color4f(0.0f, 0.0f, 0.0f))
{
		auto drawChar(SuperImage superImage, int x, int y, int f)
		{
			ubyte index = cast(ubyte) f;
			ubyte[] slice = font[(index * 5)..(index * 5 + 5)];
		
			foreach (i, e; slice)
			{
				enum ubyte mask = 0x01;
		
				foreach (j; 0..8)
				{
					auto t = e & (mask << j);
					superImage[x + cast(int) i, y + cast(int) j] = t ? foreground : background;
				}
			}
		}

		foreach (e; 0..255)
		{
			drawChar(superImage, x + 6 * e, y, e);
		}
}

Полученная картинка со всеми символами (использован настоящий размер):

Чтобы испытать вывод текста возьмем кусок текста Lorem Ipsum (известный трактат с бредятиной на латыни) и выполним ряд операций по его модификации, чтобы текст поместился в картинку без всяких проблем с «залезанием» текста за границы рисунка. Для этого мы используем функцию wrap из std.string, которая распиливает текст на строки нужного размера (внимание! именно на строки, причем буквально на строки: на выходе получаете строку, в которую на нужных «расстояниях» вставлены символы новой строки) и ряд стандартных алгоритмов над диапазонами, которые облегчат этап преобразований переменной IPSUM (в ней сохранен латинский бред):

enum IPSUM = `
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sed iaculis augue. Aenean sit amet metus ac erat blandit placerat vitae nec neque. Donec vehicula convallis justo, eget ullamcorper metus euismod quis. Nulla sodales blandit nunc, elementum volutpat urna. In pulvinar quis nunc et placerat. Vivamus enim augue, consectetur nec egestas id, tincidunt a lectus. Pellentesque nec eros rhoncus, ultrices nisi sed, pulvinar lorem.Suspendisse a felis finibus, venenatis massa ut, iaculis tortor. Nullam eget tortor nisi. Duis et urna est. Nulla dapibus quam accumsan, sagittis orci non, vulputate erat. Nulla pretium bibendum enim quis rhoncus. Donec vitae purus auctor, tincidunt quam id, commodo erat. Sed urna eros, pretium id erat et, feugiat tincidunt nunc. Mauris eget tortor vitae massa auctor rutrum vel a purus. Nam pulvinar ultrices tellus, ut aliquam tortor rhoncus nec. Aenean non cursus dolor. In dictum tortor lectus, porta bibendum augue efficitur sit amet. Pellentesque et sapien congue, feugiat est et, suscipit felis. Pellentesque sapien lacus, pretium ac molestie at, porttitor eu metus. Sed ex mauris, mattis vel enim sit amet, sagittis egestas sapien. Duis sit amet dapibus justo.Duis quis neque fermentum, hendrerit est quis, ultricies ipsum. Duis elementum augue non dui dapibus, euismod hendrerit turpis semper. Aenean malesuada, ante a blandit convallis, enim ligula hendrerit nisi, eu gravida nulla risus in sapien. Sed maximus libero libero, sit amet dictum libero condimentum quis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus ultricies quis ex tincidunt pharetra. Vestibulum gravida sem eget bibendum porta. In hac habitasse platea dictumst. Nulla semper dui suscipit nibh dictum venenatis. Vestibulum ornare feugiat tortor, ac faucibus turpis auctor eget. Nulla tristique scelerisque justo, ut ullamcorper nulla tincidunt in. Ut eu scelerisque justo. Ut sagittis ipsum ac diam viverra suscipit. Pellentesque ut purus tempus quam euismod pulvinar ac eget tortor. Fusce urna leo, posuere sed porttitor in, varius id justo. Phasellus eget sollicitudin nibh. Sed ac sem mi. Proin quam turpis, pretium sed orci vitae, ultricies rhoncus leo. Nulla vitae elementum leo. Praesent dignissim tortor id justo maximus, ac posuere dui bibendum. Etiam lobortis viverra ante. In commodo finibus metus at blandit. Vivamus ut urna erat. Integer est ligula, posuere vitae risus aliquam, euismod suscipit justo. Integer id odio semper, luctus ante non, pulvinar erat. Nullam laoreet elit at risus efficitur, vitae consequat lacus tristique. Suspendisse vel tempor sem. Integer vitae ullamcorper nisl. Suspendisse potenti. Donec sollicitudin ornare varius. Proin condimentum odio neque, quis tincidunt est vestibulum vitae. Morbi facilisis tristique velit, id ultricies turpis facilisis a.`;

void main()
{
	auto img = image(1024, 25);	
	img.testFont(5, 10);
	img.savePNG("test.png");

	auto img2 = image(1024, 512);

	foreach (x; 0..img2.width)
	{
		foreach (y; 0..img2.height)
   		{
			img2[x, y] = Color4f(1.0f, 1.0f, 1.0f); 
		}
	}

	import std.algorithm;
	import std.range;
	import std.string;

	IPSUM
		.wrap(80)
		.splitLines
		.enumerate(2)
		.each!(a => img2.drawString(5, cast(int) (a[0] * 15), a[1], Color4f(0.8f, 0.0f, 0.97f), Color4f(1.0f, 1.0f, 1.0f)));

	img2.savePNG("test2.png");	
}

Результат эксперимента:

Таким образом, мы научили dlib выводить некоторый текст в картинку, пусть и достаточно прямым путем, но все равно это тоже познавательно и интересно.

Добавить комментарий