C#, WindowsForms, наложение изображений, изменение размеров изображения.

Задали вопрос, а можно ли в C# программными средствами наложить 2 изображения друг на друга. И даже сами потом предложили какое-то жуткое решение с привлечением WinAPI, и чуть ли не ассемблера со злыми духами.

На самом деле, задача вполне себе решается стандартными средствами.

Наложение изображений

Итак, предположим, что у нас есть 2 изображения, оба они PNG с прозрачностью, и лежат в ресурсах нашего приложения. Например, флаг:

и герб:

под именами, соответственно Properties.Resources.flag и Properties.Resources.trizub_small

Сначала сделаем из изображений два объекта Bitmap:

//Берем целевое изображение
Bitmap TargetBitmap = Properties.Resources.flag;

//Берем накладываемое изображение
Bitmap OverlayBitmap = Properties.Resources.trizub_small;

Теперь надо создать результирующее изображение (оно будет пока пустым) нужного размера:

//Создаем результирующее изображение (пока пустое)
Bitmap ResultBitmap = new Bitmap(TargetBitmap.Width, TargetBitmap.Height,
            PixelFormat.Format32bppArgb);

Откуда взяли высоту и ширину — понятно, третий параметр PixelFormat берется в зависимости от исходных изображений. Желательно, чтоб они совпадали по глубине цвета, иначе получится некрасиво, может потеряться прозрачность или произойти еще какая-нибудь бяка. Я сделал 2 изображения с прозрачностью (ARGB) и глубиной цвета 32 бита.

Теперь нужно создать объект Graphics, который и будет заниматься совмещением изображений. Раз мы будем рисовать в пустом Bitmap ResultBitmap, то и объект Graphics создаем из него, воспользовавшись методом Graphics.FromImage():

//Создаем объект Graphics из результирующего изображения
Graphics graph = Graphics.FromImage(ResultBitmap);

Далее объект graph надо настроить:

//настраиваем метод совмещения изображений
graph.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;

Если вместо SourceOver установить SourceCopy, то потеряется вся прозрачность у накладываемого изображения, и будет некрасиво:

Далее, производим отрисовку:

//рисуем основное изображение
graph.DrawImage(TargetBitmap, 0, 0);

//рисуем накладываемое изображение
graph.DrawImage(OverlayBitmap, (TargetBitmap.Width-OverlayBitmap.Width)/2,
(TargetBitmap.Height-OverlayBitmap.Height)/2,
OverlayBitmap.Width,OverlayBitmap.Height);

Думаю, откуда взяты все координаты и размеры, понятно.

Осталось только присвоить Bitmap'ы PictureBox'ам

Изменение размеров изображения

Тут тоже ничего сложного и сверхъестественного нет.

Чтоб два раза не вставать, возьмем полученное выше изображение и уменьшим его:

//задаем новые размеры
int NewWidth = ResultBitmap.Width / 2;
int NewHeight = ResultBitmap.Height / 2;
//Настраиваем PictureBox для вывода уменьшенного изображения
pbResize.Size = new Size(NewWidth, NewHeight);

Создадим новый Bitmap для будущего уменьшенного изображения:

//создаем новый Bitmap для измененного изображения
Bitmap ResizeBitmap = new Bitmap(NewWidth,
NewHeight);

Опять создадим объект Graphics, который будет заниматься отрисовкой:

//создаем объект Graphics, который будет изменять размер
Graphics ResizeGraph = Graphics.FromImage(ResizeBitmap);

Поставим повыше качество изображения:

//ставим высокое качество
ResizeGraph.InterpolationMode =
System.Drawing.Drawing2D.InterpolationMode.High;

И делаем отрисовку:

//рисуем изображение с измененным размером
ResizeGraph.DrawImage(ResultBitmap, 0, 0, NewWidth, NewHeight);

В заметке я пропустил вывод изображений в PictureBox'ы, но он и так очевиден (в исходнике есть).

Вот, что получилось:

Исходник примера на GitHub

C#, WindowsForms. Автоматическая выгрузка и загрузка содержимого контролов на форме. В объект или запись DataSet.

Решил побороть еще одно узкое место в коде, которое приводит к невероятному количеству ручного кодинга, а именно — загрузка данных из записи DataSet или произвольного объекта в форму, для изменения/ввода данных пользователем, а потом обратная выгрузка.

Вообще, долго думал и гуглил, как сей неприятный процесс автоматизировать, но почему-то везде предлагали либо встроенные, либо сторонние генераторы кода, т.е. ответ был «генерируй!». Но меня такой ответ не устроил, неужто, никак нельзя все это более-менее автоматизировать, не прибегая к внешним инструментам.

Оказывается, можно. Все инструменты для этого есть — есть System.Reflection, через инструменты данного пространства имен можно получать имена свойств или полей нужного класса, а также тип данных, и есть методы для поиска нужных контролов на форме, чтобы загрузить или сохранить данные в/из объекта.

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

Опять же, тут будет краткое рассуждение, а пример кода в конце.

Основные компоненты

Основные компоненты это:
— текстовые поля, куда можно ввести либо строку, либо число (фильтрацию ввода оставим форме)
— checkbox’ы, хранящие булево значение
— radiobutton’ы/переключатели — ограниченный выбор из определенного набора вариантов.

От этой печки танцевать и будем, если понадобится что-то еще — это будет уже проще свести к этим трем типам, или, немного изменив код, добавить конкретный случай в класс-прослойку.

Как хранить набор вариантов

Тут уже придумали все за нас, есть прекрасный тип Enum, который, собственно, для этого и предназначен. Например, нам надо будет хранить тип соединения с сетью. Можно сделать вот такое перечисление:

public enum NetConnectionType
{
    NoProxy = 0,
    SystemProxy = 1,
    ManualProxy = 2
}

Соглашение об именах

Это единственный наглый момент во всем примере — контролы придется называть не абы как, а по правилам, впрочем, правила всегда можно переопределить, и сделать удобные вам. Я делал удобно для себя.
Для TextBox‘ов и CheckBox‘ов правила такие: сначала идет префикс txt или chk, далее — название поля в записи таблицы DataSet или название свойства объекта, например chkAutorun или txtUserName. В классе, соответственно, должны быть поля bool Autorun или string UserName.

Для радиокнопок (переключателей) имя формируется по следующему принципу: префикс rb+НазваниеСвойства+ЗначениеВEnum, т.е., например, радиокнопка, указывающая на прямое соединение, будет называться rbConnectionNoProxy

Думаете, сложно и длинно? Ну, может быть, только сталкиваешься с этим потом один раз, когда моделируешь форму.

Разные вспомогательные мелочи

Получение списка радиокнопок и поиск контрола на форме Копия

Получение значения Enum из состояния RadioButton:

private string GetEnumValFromRb(string PropName)
{
    string EnumVal = string.Empty;
    string rbName = "rb"+PropName;            
    
    foreach (RadioButton rb in RadioButtons)
    {                
        if (rb.Checked)
        {
            if (rb.Name.StartsWith(rbName))
            {
                EnumVal = rb.Name.Substring(rbName.Length);
            }
        }
    }

    return EnumVal;
}

Весь код примера

На GitHub

C#, WindowsForms — найти все переключатели (RadioButtons) на форме.

Простой поиск контрола

Если нам нужно найти контрол на форме, зная его имя, то все решается довольно просто — у массива контролов Controls есть метод Find, который найдет нам что нужно, если указать правильное имя контрола:

private Control FindControl(string ControlName, Form form)
{
    Control ctrl = null;

    Control[] buf = form.Controls.Find(ControlName, true);
    if (buf.Length == 0) return null;
    if (buf.Length > 1) return null;

    ctrl = buf[0];

    return ctrl;
}

Поиск всех RadioButton’s (или других однотипных контролов)

Вот тут уже сложнее, особенно с переключателями. Они обычно сидят на форме в контейнерах, например в GroupBox'ах, и функция Find тут не поможет. Необходим другой подход, если мы хотим получить список контролов определенного типа. А именно — надо сделать рекурсивную функцию поиска. Кто боится рекурсии и связанных с ней переполнений, скажу, что ничего страшного нет.
Мне удалось уронить студию на 5 000 однотипных компонентов, а подобное число компонентов вряд ли может быть в реальности, только если вы не радиокнопочный маньяк 🙂

Функция такая вот:

private List FindAllRadiobuttons(Control.ControlCollection collection)
{
    List  result = new List();
    foreach (Control ctrl in collection)
    {
        if (ctrl.HasChildren)
        {
            result.AddRange(this.FindAllRadiobuttons(ctrl.Controls));
        }
        if (ctrl is RadioButton)
        {
            result.Add((RadioButton)ctrl);
        }
    }

    return result;
}

Т.е. если мы просто наткнулись на переключатель, при переборе контролов из массива Controls, то добавляем переключатель в массив, если же, мы наткнулись на контрол-контейнер (ctrl.HasChildren == true), то вызываем функцию перебора массива уже для массива Controls контейнера.

C#. О конфигах и сохранении/загрузке свойств объекта, часть 2

В прошлой части Копия я рассказывал, как с помощью инструментов из пространства имен System.Reflection, можно сохранить свойства объекта, например, в таблицу DataSet, или наоборот, загрузить из DataSet данные в свойства соответствующего объекта. Таким образом, решалась проблема автоматизации работы с конфигурационными файлами.

Есть способ еще более уменьшить количество кода, воспользовавшись стандартным механизмом .NET Framework — сериализацией. Сериализация, это, по рабоче-крестьянски говоря, именно что сохранение состояния объекта (он же пафосно называется «экземпляром класса») в некий передаваемый формат. Доскональное объяснение, что это такое, в статью не влезет, потому оставим.

Итак, переходим к сериализации.

В .NET Framework сам себя класс сериализовать не может, точнее, сериализовать-то может, а вот десериализовать — нифига. Класс и его экземпляр, получается, как Штирлиц с раненой радисткой Кэт, передать могут, а обратно нет, без дружественной помощи.

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

Итак, создадим класс-хранилище, вот такой вот, например:

[Serializable]
public class AppSettings
{
    public string DataUrl { get; set; }
    public FormatType DataFormat { get; set; }
    public string IPColumn { get; set; }
    public string FieldSeparator { get; set; }
    public string FlagColumn { get; set; }
    public string TrueValue { get; set; }
    public string FalseValue { get; set; }
    public bool LoadUpdate { get; set; }

    public AppSettings()
    {
    }
}

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

[Serializable]

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

Ну, раз уж в прошлый раз, мы выбирали XML, то и сейчас я буду сериализовывать класс в XML

Особенности сериализации в XML в .NET Framework

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

Если какое-то публичное свойство не надо сериализировать, т.е. в нашем случае, сохранять в конфиг, то такому свойству надо установить атрибут [XmlIgnore], для публичных полей устанавливается атрибут [NonSerialized].

Например:

[XmlIgnore]
public string DataUrl { get; set; }

Последняя особенность сериализации в XML — сериализируемому классу необходим конструктор без параметров, причем, практически выявлено, в этом конструкторе лучше вообще ничего не делать, особенно того, что может привести к ошибкам времени выполнения. Иначе получите гадскую невыявляемую ошибку, поскольку в отладке у вас будет всякая фигня, кроме того, чего нужно.
Я так понимаю, конструктор без параметров вызывается во время XML-сериализации, и наверняка, в нем что-то можно и нужно делать, но пока я не нашел, как и где это подробно расписать.

Класс-менеджер

Само дерево жужжать не может
Значит, кто-то тут жужжит
(Вини-Пух)

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

this = (Data)readerRr.Deserialize(fileRr);
this - переменная только для чтения, по крайней мере до .NET 4.0 включительно.

И такой подход считается «плохим дизайном», хотя на мой нескромный взгляд, плохой дизайн — это разносить части одного и того же по разным классам.

Но раз уж надо, значит надо. Делаем класс-менеджер:

Далее такой условный класс-менеджер с возможностью сохранения и загрузки:
Читать далее

Исходник примера на GitHub

AppSettings.cs

Источники

Киберфорум
Сериализация в XML. XmlSerializer

Навел на мысль [info]steinkrauz@ljr

L.O.S.A.I.G!

Офигенная эмо-панк группа из моего города.

Тащемта, были даже на дребедне нашего города, было холодно и неприятно (от погоды), но вообще парни пиздатые. Особенно вокалист XD

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

Прямая ссылка: кликать сюда

С днем настоящего карельского флага


В этом году совпавшим еще и с летним Солнцестоянием

Созвездие северной свободы
21 июня 2018 года исполняется столетие первого флага Карелии — синего полотнища, на котором расположены семь серебряных звезд Большой Медведицы (Otava). Впервые этот флаг был поднят над селом Ухтуа летом 1918 года, а его автором стал финский художник Йонас Хейска. Он пояснял, что на эту символику его вдохновили руны «Калевалы», где упоминается это созвездие.

Я, как всегда немного опаздываю, потому что у меня график в интернетах абсолютно не совпадает с планетой 🙂

Окончательно закрыли сайт Hexproject’а

На сайте был старый движок, бесплатные хостинги какашка, да и весь кодинг (даже с приставками «быдло-» и «около-«) можно выложить на Гитхаб, и не надо что-то отдельное изобретать.

Виртуальная сим-карта для регистрации на сервисах, требующих номер телефона.

Мои друзья делают замечательный проект, способствующий сохранению анонимности в Интернете — virtualsim.net. На этом сайте вам за небольшую сумму зарегистрируют номер телефона на год, на который можно будет регистрироваться на всяких сайтах и сервисах, требующих номер телефона (Telegram, Vkontakte, OK.RU и т.д.)

Мы вот таким образом обзавелись телеграм-каналом и страницей во Vkontakte.

У ребят огромная база данных (на несколько тысяч номеров), есть возможность выбрать страну. В настоящий момент доступны Украина, Россия, Казахстан, Латвия. При офигенно качественном и важном сервисе, избавляющем, наконец, от мобильного рабства, цены вполне демократичны: Минимальный тариф за номер — 5$ в год для некоммерческой деятельности, например, регистрации страницы в VK, для коммерческой деятельности подороже, но вполне терпимо. Если честно, дешевле я не нашел. Постоянным клиентам предоставляются скидки. Впрочем, дублировать весь прайс в данной заметке я не буду. Если вас заинтересовал сервис, пройдите по ссылке ниже

virtualsim.net

VIRTUALSIM.NET ОСТАВАЙСЯ АНОНИМНЫМ!

DetecTOR, утилита, определяющая, относится ли IP к сети TOR

Написана изначально была аж в 2013 году и довольно кривовато, но по многочисленным просьбам нашего дорогого зрителя, была переделана, с подробными объяснениями по поводу «как», «что», «где» и «куда».

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

Выглядит главное окно вот так:

readme.txt

Чтоб два раза не вставать.
readme.txt

Смотреть исходники (на GitHub)
Скачать программу (Win32) c GitHub

Открытый отчет 2017-2018 обновление

Теперь больше не должны 10 $, нам как-то быстро за день надонатили, после публикации открытого отчета, что мы даже чихнуть не успели.

Благодарим неравнодушных людей. Зала славы и публикации имен, естественно, не будет. Как и открытых реквизитов для доната (до сентября)

Открытый отчет 2017-2018

Итак, на жизнь сайту надо 60 $ в год, по итогам года собрано 50 $, 10 остались должны, но это некритично.

Пришлось урезать расходы на админов, занимающихся бэкендом и дизайном, поэтому Нуген и Асперг более не являются сотрудниками редакции.

Состав редакции:
Администратор, директор проекта: Алексей Тихонов, (aka Leha Silent) Костомукша/Tampere
Главный редактор: Анатолий Перец, (aka PunkArr[]) СПб/Кондопога
Заместитель главного редактора: Lina Lukina (Keronen) (aka Lin’Ka) Tampere
В команду пришел новый человек, консультант по IT: N.K.T, aka Nikita N. (MSK/SPb/PTZ)

Супермолния над Кондопогой

Крупный вариант доступен по клику на фото.
Автор фото: Зелёнкин Я.Н. Все права сохранены. Публикация изначально запрещалась, но чтоб сохранить замечательное фото, я решил выложить в открытый доступ.

Байка про порчу.

Или о том, как я стал колдуномЪ.

В свои 17 лет я действительно интересовался непознанным, гадал на картах знакомым (лет с 12), и правда верил в колдовство (и сейчас верю, с некоторыми поправками).
Надо допустить небольшую ремарку, что в школе я был почти круглый отличник, исключая математику (по ней совокупно натянули итоговую 4, а так я ее знаю на 2 с плюсом), и ИЗО (по которому у меня была твердая двойка, которую переправили на тройку, благодаря влиянию отца-военного). Но дело не в этом.

В школе я был няшным мальчиком, вежливым и интеллигентным. Участвовал в конференциях и олимпиадах, где периодически занимал призовые места (это важно).
Но вот, подошел 11 класс, и я решил поступить на физтех. В военное училище не взяли по здоровью, а на биофак, куда я хотел и мог, жутко отговаривала вся семья (идиоты, нахуй я их послушал). Мои знания в физике были довольно малы, потому мама отсыпала мне бабла на репетитора. Поскольку, роль играло бабло, то пропускать репетитора можно было только в случае смерти.

Итак, суть. Последним уроком шла история, и тут нашему директору пришла в голову мысль припахать меня на спонсорский КВН. Тогда такое дело было в почете — для спонсоров школы проводились показушные мероприятия. А мне надо было идти к репетитору (Бабло!). Тем более все эти КВН-ы мне нахуй не вперлись.

Далее рассказываю в лицах (И — историчка, пламенная едроска, и Я — это я):

И: Васисуалий Пропердыщев, вас выбрали от класса на КВН.
Я: Я не могу участвовать в КВН, у меня репетитор по физике
И: Вы можете пропустить занятие, это ЧЕСТЬ ШКОЛЫ!
Я: За это занятие заплачены деньги, я не могу его пропустить.
И: Тебе что, ДЕНЬГИ важнее ШКОЛЫ?
Я: Да, важнее, их зарабатывали мои родители.
Я: И вообще, я столько в конференциях и олимпиадах участвовал, что, может быть, Вы освободите меня от участия в этом КВН?
И: Олимпиады и конференции — это твое личное дело! Они школе не нужны!

И тут у меня, простите, бомбануло. В нашей школе было очень плохо с информатикой, и я ее изучал, по протекции мэрии, в Лицее, где были относительно нормальные компы (Mac LC 2/3), и преподавали мне довольно крутые женщины, я начал делать даже собственные сайты на Народе, web 1.0. Сделал сайты паре магазинов (за что меня отблагодарили фотографией молнии), и панковской группе (но это позже описываемых событий).

Я: Ох, раз они вам не нужны, тогда я на городской конференции буду выступать за Лицей!

И тут у К█████й, исторички, женщины, внешностью и размерами похожей на йоркширского терьера, началась форменная истерика. Она обозвала меня фашистом (хорошо, что Правого Сектора тогда не было), я ей ответил, «а вы не видите, что я скинхед», и провел рукой по своей шикарной шевелюре, которую не стригли лет с 14.

Первая часть истории закончилась тем, что К█████а убежала стучать на меня директору, класс с которым я никогда не дружил, высказал мне «фи», а я ушел учить физику.

Потом был зачет по истории, и мстительная собачка меня таки завалила, на что я забил, хотя пытался ситуацию исправить, но собачка была непреклонна, и на исправления, я, либо «опаздывал», либо «ее не было».

А теперь про порчу

Сижу я такой грустный в коридоре, и ко мне подходит моя классная (действительно классная) руководительница.
КР: У тебя двойка по истории за четверть, может я с К█████й поговорю?
Я: █.█. Не надо, я (наматываю на палец цепочку с перевернутым крестом) сам с ней разберусь, на нее порчу наведу.

На следующий день «собачка» попала в больницу с осложнением после гриппа.

Хотя я ничего даже не колдовал, просто пошутил. Но интересно, что зачет по истории вдруг был исправлен автоматически. Т.е. я пришел исправлять, а «собачка» мне говорит, что все уже исправлено.
«Осиное гнездо» — партия училок, которые держали всю школу (туда входила «собачка», химичка, информатичка, а шестерками у них были две учительницы биологии), стало ко мне относиться хорошо. И химичка — главная оса в осином гнезде, с перепугу даже отказалась со мной фотографироваться на выпускном.

Кстати, химичка, когда-то, как потом выяснилось, пыталась увести мужа у моей тетушки, которую в селе Г██████ро все считали ведьмой. Но не получилось. Подробностям этой истории должна быть посвящена отдельная статья.

Вот такие вот дела!

C#, Симпатичное окошко «О программе»

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

Смотреть анимацию

На самом деле, форма была устроена ужасно, написана совсем другими людьми для другого проекта, а у нас кочевала из программы в программу без особых изменений, строки добавлялись в 3 ArrayList в коде формы, в функции рисования были какие-то непонятные жестко заданные поправочные коэффициенты, подобранные на глаз, и т.д.
Решили немного поправить и все переделать. Не знаю, насколько получилось лучше, но понятнее и универсальней точно. Далее будет не столько код с пояснениями, сколько попытка показать, как мы рассуждали, переделывая форму.

Определение сущностей

Сначала надо определить с какими данными мы будем работать. В первую очередь, мы выводим набор строк, т.е. первая сущность это строка, у строки может быть задан шрифт и цвет текста, и, собственно, содержимое строки — это будут явные параметры сущности. В процессе добавился еще один, неявный параметр — номер сцены

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

Параметры шрифта это FontFamily (Arial, Times New Roman и т.д.), размер и начертание или стиль (жирный, курсив, подчеркнутый, зачеркнутый, или все вместе). Для того, чтобы связать шрифт со строкой, пришлось добавить еще один параметр внутреннее имя, по которому программа будет узнавать, какой шрифт к какой строке применить.

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

Формат данных

Негоже задавать эту кучу параметров непосредственно в коде, поэтому пришлось подумать над форматом данных. Решено было хранить описание строк, шрифтов и сцен в виде простого текстового файла следующего формата:

...
команда;параметр1;параметр2;параметр3 [...]
команда;параметр1;параметр2;параметр3 [...]
команда;параметр1;параметр2;параметр3 [...]
...

Формат команд следующий (в квадратных скобках необязательные параметры):

Шрифт:
addfont; FontFamily; Размер (pt, float); [Стиль]; Внутреннее_имя
Например:
addfont; Arial;9;Bold;Group;
addfont; Microsoft Sans Serif;8;;Names;
addfont; Arial;14;Bold italic;Slogan;

Строка:
addstring; Строка; [Имя_шрифта]; [цвет_текста];
Например:
addstring;Make code, not war! C# like you.;Slogan;FF FF 00;

Сцена:
scene; [цвет_фона];[скорость_таймера_прорисовки ms];[пауза ms]
Например:
scene;20;3000;

Цвет текста задается в шестнадцатеричном формате через пробел(ы): R G B [Alpha]
Например:
FF FF 00 — желтый
FF 00 00 80 — красный с прозрачностью

Начертания (стили) шрифта перечисляются через пробел(ы), от регистра не зависят.

Структуры для хранения данных

Шрифты:
Генерируются при анализе «скрипта», и помещаются в приватный Dictionary, где строковый ключ — заданное «внутреннее имя» шрифта.

Строка:
Текст и параметры хранятся в специальной структуре:

private struct AboutString
{
    public string Text;
    public string FontName;
    public Color TextColor;
    public int SceneNumber;
}


А все строки в private List Strings

Сцена:
Для описания сцены так же создана структура:

private struct AboutScene
{
    public int PauseTimeout;
    public int StringsHeight;
    public int StringsCount;
    public Color BackColor;
    public int DrawTimeout;
}

И описания сцен так же хранятся в private List Scenes

Заметки про парсинг

— последняя точка с запятой является необязательной
— пустые строки, или строки, содержащие только пробельные символы, пропускаются
— команды приводятся к нижнему регистру (т.е. они регистронезависимы)
— есть возможность оставить однострочный комментарий, начинающейся с ~ (тильды). Все, что после знака ~ считается комментарием и пропускается при анализе «скрипта».
— если первая (нулевая) сцена явно не определена, то создается сцена с параметрами по умолчанию

Команды, благо их немного, последовательно анализируются с помощью конструкции switch/case и передаются в функции добавления шрифта, строк, или сцены, где анализируются далее.
Параметры и команда разбираются с помощью string.split(';');

Некоторые вспомогательные функции для парсинга

Размер шрифта должен быть float, поэтому надо сделать функцию-обертку над Convert, так, как я писал об этом ранее:

private float ConvertToFloat(string Number)
{
    NumberFormatInfo format = new NumberFormatInfo();
    format.NumberDecimalSeparator = ".";
    return (float)Convert.ToDouble(Number, format);
}

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

private string RemoveDupSpaces(string s)
{
    while (s.Contains("  "))
    {
        s = s.Replace("  ", "");
    }

    return s;
}

Заметки о реализации

Естественно, весь функционал не стоит реализовывать прямо на форме, потому вся работа вынесена в отдельный класс AboutDrawer, который парсит переданный скрипт и занимается отрисовкой строк на форме.
На форме строки отображаются в компоненте PictureBox, который передается в конструктор класса. Конструктор также устанавливает некоторые параметры по умолчанию.
Поскольку, класс все равно работает с компонентами формы, то спокойно подключаем пространство имен System.Windows.Forms, и пользуемся всеми его возможностями. Например, в классе создаются два таймера (Timer). Внутри обработчика события Tick одного из них, происходит отрисовка, а другой используется для эффекта паузы.
В классе созданы открытые свойства для установки цвета фона, цвета текста по умолчанию, и размера расстояния между строк.

Подробнее ознакомиться с кодом примера можно на Github:

Код на GitHub

Об анимированные GIF

Народ, а куда вы постите анимированные GIF? Вот мне никогда не надо было. Я вообще не люблю тяжелых медиа, всяких там скриптов и флешей, но тут вдруг понадобилось. Довел гадский ютуб, безбожно пережимающий видео записей с экрана, во что-то мерзкое и непотребное, где ничего не разглядеть.

Решил проверить, как с этим делом у imgur, который стандартный хостинг для изображений на работе и в блоге, но тот перекодировал GIF во что-то свое, сильно уж вебдванольное, которое в моем браузере отказалось запускаться (но запустилось в Хроме).

Любители современных медиа, у которых смартфоны, и они из них не вылазят, насоветовали Giphy. Вроде работает, действительно загружает и показывает GIF, как GIF, а не как нечто неведомое.
Но неужто, на весь интернет, это единственный хостинг для анимированных GIF? Не верю.

И, предупреждая комментарии, о mail.тьфу или известной тьфунатебяльной сети, я про них знаю, но не подходит, по причине месторасположения. Тьфу на тебя, ГОРФ.