Генератор фоновых изображений

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

Содержание

Описание приложения «Генератор фоновых изображений»

Приложение состоит из двух форм. В главной форме выбираем вариант изображения, задаём цвета фона и символов, по кнопке вызываем форму просмотра. Настройки приложения сохраняются в файле конфигурации. В форме просмотра по таймеру меняется изображение в соответствии с выбранным вариантом. Запуск и остановка изменений происходит по клику мышки. Один из вариантов главного окна приложения приведён на рис. 1 ниже :

рис. 1 Так выглядит наше приложение

Запускаем Microsoft Visual Studio и создаём новое Windows Forms App. Я назвал его GenBG.

Создание главной формы приложения

Макет главной формы приложения должен в результате иметь вид как на рис. 2 ниже :

рис. 2 Главная форма приложения

Добавим на форму в соответствии с макетом ComboBox для выбора вариантов изображений, кнопку запуска просмотра, метки и текстовые поля для настройки цветов и выбора текстового файла, несколько PictureBox для отображения выбранных цветов, CheckBox для определения режима градиентных цветов кистей. Для выбора цвета используем стандартный ColorDialog, а для выбора файла — OpenFileDialog. По своему усмотрению скорректируйте иконку приложения. Запретим изменять размер нашего окна в настройках. FormBorderStyle — Fixed3D. MaximizeBox — False. В ComboBox добавим варианты для выбора изображений — 5 видов как на рис. 3 ниже :

рис. 3 Варианты изображений.

Для изменения цвета будем вызывать ColorDialog по клику в текстовых полях отображения цветов фона и символов или по кнопкам ‘Выбрать’ для цветов 1 и 2. Цвет 1 для градиентной фоновой кисти — цвет в нижней части. Цвет 2 для градиентной символьной кисти — тоже в нижней части. Старое значение цвета передаём в ColorDialog при его вызове, а новое значение после успешного выбора ( DialogResult.OK ) отображаем в текстовом виде и перерисовываем графические поля. Имя файла и путь к нему меняем по клику в текстовом поле для его отображения с помощью OpenFileDialog. Если галочка в поле Градиент не установлена, то поля и кнопки < Цвет 1, 2 > не активны. Для обработки перечисленных выше событий в конструкторе создадим соответствующие методы. Реализация кода класса для главной формы приведена ниже :

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GenBG
{
    public partial class MainFormGenBG : Form
    {
        Color col_bg = Color.White;
        Color col_sim = Color.Black;
        Color col1 = Color.DarkBlue;
        Color col2 = Color.LightBlue;
        bool grad = true;
        string nameFile = "";
        int mode = 0;

        AppCfg m_cfg = new AppCfg();
        public MainFormGenBG()
        {
            InitializeComponent();
        }

        public string colToString(Color col)
        {
            return string.Format("#{0:X06}", col.ToArgb());
        }
        private void butView_Click(object sender, EventArgs e)
        {
            ViewGenForm dlg = new ViewGenForm();
            m_cfg.Col1 = col1;
            m_cfg.Col2 = col2;
            m_cfg.Col_bg = col_bg;
            m_cfg.Col_sim = col_sim;
            m_cfg.NameFile = nameFile;
            m_cfg.Grad = grad;
            m_cfg.Mode = mode;

            dlg.cfg = m_cfg;
            dlg.BackColor = col_bg;
            dlg.ShowDialog();
        }

        private void butSelCol1_Click(object sender, EventArgs e)
        {
            colorDialog1.Color = col1;
            if (colorDialog1.ShowDialog() == DialogResult.OK)
            {
                col1 = colorDialog1.Color;
                text_col1.Text = colToString(col1);
                pictureBoxGrad.Refresh();
            }
        }

        private void butSelCol2_Click(object sender, EventArgs e)
        {
            colorDialog1.Color = col2;
            if (colorDialog1.ShowDialog() == DialogResult.OK)
            {
                col2 = colorDialog1.Color;
                text_col2.Text = colToString(col2);
                pictureBoxGrad.Refresh();
            }
        }

        private void checkBox_grad_CheckedChanged(object sender, EventArgs e)
        {
            grad = checkBox_grad.Checked;
            labelCol2.Enabled = grad;
            text_col2.Enabled = grad;
            butSelCol2.Enabled = grad;
        }

        private void comboBoxMode_SelectedIndexChanged(object sender, EventArgs e)
        {
            mode = comboBoxMode.SelectedIndex;
        }

        private void textColBG_MouseClick(object sender, MouseEventArgs e)
        {
            colorDialog1.Color = col_bg;
            if (colorDialog1.ShowDialog() == DialogResult.OK)
            {
                col_bg = colorDialog1.Color;
                textColBG.Text = colToString(col_bg);
                pictureBoxBG.BackColor = col_bg;
                pictureBoxBG.Refresh();
                pictureBoxGrad.Refresh();
            }
        }

        private void textColSim_MouseClick(object sender, MouseEventArgs e)
        {
            colorDialog1.Color = col_sim;
            if (colorDialog1.ShowDialog() == DialogResult.OK)
            {
                col_sim = colorDialog1.Color;
                textColSim.Text = colToString(col_sim);
                pictureBoxSim.BackColor = col_sim;
                pictureBoxSim.Refresh();
                pictureBoxGrad.Refresh();
            }
        }

        private void pictureBoxGrad_Paint(object sender, PaintEventArgs e)
        {
            Bitmap picGr = new Bitmap(pictureBoxGrad.Width, pictureBoxGrad.Height);
            Graphics g = Graphics.FromImage(picGr);
            g.Clear(pictureBoxGrad.BackColor);

            Brush br1 = new SolidBrush(col_bg);
            Brush br2 = new SolidBrush(col_sim);

            Rectangle rc = new Rectangle(0, 0, pictureBoxGrad.Width / 2, pictureBoxGrad.Height);
            Brush grBr1 = new LinearGradientBrush(rc, col_bg, col1, LinearGradientMode.Vertical);
            g.FillRectangle(grBr1, rc);
            rc.X = pictureBoxGrad.Width / 2;
            Brush grBr2 = new LinearGradientBrush(rc, col_sim, col2, LinearGradientMode.Vertical);
            g.FillRectangle(grBr2, rc);

            br1.Dispose();
            br2.Dispose();
            grBr1.Dispose();
            grBr2.Dispose();
            e.Graphics.DrawImage(picGr, new Point(0, 0));
            g.Dispose();
            picGr.Dispose();
        }

        private void textNameFile_MouseClick(object sender, MouseEventArgs e)
        {
            openFileDialog1.Title = "Выберите файл для демонстрации набора текста";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                nameFile = openFileDialog1.FileName;
                textNameFile.Text = nameFile;
            }
        }

        private void MainFormGenBG_Load(object sender, EventArgs e)
        {
            m_cfg = SaveLoad.LoadCfg(SaveLoad.pathCfg);
            col1 = m_cfg.Col1;
            col2 = m_cfg.Col2;
            col_bg = m_cfg.Col_bg;
            col_sim = m_cfg.Col_sim;
            grad = m_cfg.Grad;
            nameFile = m_cfg.NameFile;
            mode = m_cfg.Mode;
            comboBoxMode.SelectedIndex = mode;
            checkBox_grad.Checked = grad;
            labelCol2.Enabled = grad;
            text_col2.Enabled = grad;
            butSelCol2.Enabled = grad;
            textColBG.Text = colToString(col_bg); 
            textColSim.Text = colToString(col_sim);
            text_col1.Text = colToString(col1);
            text_col2.Text = colToString(col2);
            textNameFile.Text = nameFile == null ? "Кликните, чтобы выбрать файл" : nameFile;
            pictureBoxBG.BackColor = col_bg;
            pictureBoxBG.Refresh();
            pictureBoxSim.BackColor = col_sim;
            pictureBoxSim.Refresh();
        }

        private void MainFormGenBG_FormClosing(object sender, FormClosingEventArgs e)
        {
            m_cfg.Col1 = col1;
            m_cfg.Col2 = col2;
            m_cfg.Col_bg = col_bg;
            m_cfg.Col_sim = col_sim;
            m_cfg.NameFile = nameFile;
            m_cfg.Grad = grad;
            m_cfg.Mode = mode;
            SaveLoad.SaveCfg(m_cfg);
            //MessageBox.Show("SaveLoad.SaveCfg(m_cfg)");
        }

        private void pictureBoxSim_Paint(object sender, PaintEventArgs e)
        {
            Bitmap picGr = new Bitmap(pictureBoxSim.Width, pictureBoxSim.Height);
            Graphics g = Graphics.FromImage(picGr);
            g.Clear(pictureBoxSim.BackColor);
            e.Graphics.DrawImage(picGr, new Point(0, 0));
            g.Dispose();
            picGr.Dispose();
        }

        private void pictureBoxBG_Paint(object sender, PaintEventArgs e)
        {
            Bitmap picGr = new Bitmap(pictureBoxBG.Width, pictureBoxBG.Height);
            Graphics g = Graphics.FromImage(picGr);
            g.Clear(pictureBoxBG.BackColor);
            e.Graphics.DrawImage(picGr, new Point(0, 0));
            g.Dispose();
            picGr.Dispose();
        }
    }
}

Создание формы просмотра изображений

Добавим в проект ещё 1 форму, в которой будем просматривать выбранный вариант изображения. Форма состоит из области для рисования PictureBox, которая заполняет всю клиентскую часть и таймера. Макет формы на рис. 4 ниже :

рис. 4 Форма для просмотра изображений

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

    public partial class ViewGenForm : Form
    {
        public AppCfg cfg = null;
        string txt = "";
        string[] ar_txt = null;
        bool work = false, bSave = false;
        int ind_txt = 0;
        int[] nn = new int[10000];
        int f_str = 0;
        int last_str = 0;
        int cur_pos = 0;
    ...
        private void ViewGenForm_Load(object sender, EventArgs e)
        {
            if (cfg != null)
            {
                if (File.Exists(cfg.NameFile))
                {
                    using (var sr = new StreamReader(cfg.NameFile))
                    {
                        txt = sr.ReadToEnd();
                        ar_txt = txt.Split('\n');
                    }
                }
                if ((cfg.Mode == 1) || (cfg.Mode == 3))
                {
                    Random rnd = new Random((int)(DateTime.Now.Ticks % 10000000));
                    int l_str = (int)(pictureBox1.Width / 13) + 30;
                    ArrayList al = new ArrayList();
                    //string sn = "";
                    int i, j;
                    for (i = 0; i < 10000; i++)
                    {
                        nn[i] = rnd.Next(0, int.MaxValue);
                    }
                    if (cfg.Mode == 1)
                    {
                        StringBuilder sb = new StringBuilder();
                        for (i = 0; i < 10000; i++)
                        {
                            string s = nn[i].ToString();
                            for (j = 0; j < s.Length; j++)
                            {
                                if (rnd.Next(0, 20) > 10) sb.Append(" " + s.Substring(j, 1));
                                else sb.Append(s.Substring(j, 1));
                            }
                            if (sb.Length > l_str)
                            {
                                al.Add(sb.ToString()); sb.Clear();
                            }
                        }
                        ar_txt = (string[])al.ToArray(typeof(string));
                    }
                    if (cfg.Mode == 3)
                    {
                        StringBuilder sb = new StringBuilder();
                        for (i = 0; i < 10000; i++)
                        {
                            sb.Append(Convert.ToString(nn[i], 2));
                            if (sb.Length > l_str)
                            {
                                al.Add(sb.ToString()); sb.Clear();
                            }
                        }
                        ar_txt = (string[])al.ToArray(typeof(string));
                    }
                }
                if ((cfg.Mode == 2) || (cfg.Mode == 4))
                {
                    Random rnd = new Random((int)(DateTime.Now.Ticks % 10000000));
                    timer1.Interval = 200;
                    for (int i = 0; i < 10000; i++)
                    {
                        nn[i] = rnd.Next(0, 800);
                    }
                }
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            pictureBox1.Refresh();
        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {
            if (work)
            {
                work = false;
                bSave = true;
                Thread.Sleep(2000);
                timer1.Stop();
                this.Text = "Результат генерации + " + "Стоп -> Кликни, чтобы начать";
            }
            else
            {
                work = true;
                timer1.Start();
                this.Text = "Результат генерации + " + "Старт -> Кликни, чтобы закончить и записать изображение в файл";
            }
        }
    }

Генерация всех изображений происходит в методе Paint. Об особенностях генерации каждого их вариантов остановлюсь отдельно ниже. В начале создаём область для рисования в памяти, кисти и перья, шрифт, фон прорисовываем цветом фона. Далее если конфигурация приложения передана, то в зависимости от её значений создаём для фона и символов обычные или градиентные кисти и несколько служебных переменных. Далее в зависимости от выбранного режима рисуем нужное изображение в памяти и потом выводим уже готовое изображение в окно формы. Если вывод (таймер) был остановлен, то записываем в файл битовый рисунок выбранного изображения в момент остановки. В конце подчищаем память от ненужных уже графических объектов.

private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
    Bitmap picGr = new Bitmap(pictureBox1.Width, pictureBox1.Height);
    Graphics g = Graphics.FromImage(picGr);
    Pen penGr = new Pen(Brushes.Yellow, 3);
    Pen penSn = new Pen(Brushes.Red, 3);
    Pen penBlack = new Pen(Brushes.Black, 3);
    SolidBrush brRED = new SolidBrush(Color.Red);
    SolidBrush brGR = new SolidBrush(Color.Green);
    SolidBrush brLY = new SolidBrush(Color.Yellow);
    SolidBrush brBlack = new SolidBrush(Color.Black);
    Font fN = new Font(FontFamily.GenericMonospace, 13.0F, FontStyle.Bold);

    g.Clear(pictureBox1.BackColor);

    if (cfg != null)
    {

        Rectangle rc_bg = new Rectangle(0, 0, pictureBox1.Width, pictureBox1.Height);
        Brush lgb_bg = new LinearGradientBrush(rc_bg, cfg.Col_bg, cfg.Col1, LinearGradientMode.Vertical);
        Rectangle rc_sim = new Rectangle(0, 0, 10, 20);
        Brush lgb_sim = new LinearGradientBrush(rc_sim, cfg.Col_sim, cfg.Col2, LinearGradientMode.Vertical);
        Pen penSim = new Pen(lgb_sim, 3);
        Point p1 = new Point(0, 0);
        int nr = 0, nc = 0, n_str;
        int l_str = (int)(pictureBox1.Width / fN.SizeInPoints) + 30;
        string ss = "", sl = "";
        if (cfg.Grad == false)
        {
            lgb_bg.Dispose();lgb_sim.Dispose();
            lgb_bg = new SolidBrush(cfg.Col_bg);
            lgb_sim = new SolidBrush(cfg.Col_sim);
        }
        g.FillRectangle(lgb_bg, rc_bg);
     ...

        lgb_bg.Dispose();lgb_sim.Dispose();
    }

    if (bSave)
    {
        bSave = false;
        string sName = string.Format("proba{0:D}.png", cfg.Mode);
        picGr.Save(sName, System.Drawing.Imaging.ImageFormat.Png);
        this.Text = "Результат генерации + " + "Стоп -> Кликни, чтобы начать (Tick) " + sName;
    }
    e.Graphics.DrawImage(picGr, new Point(0, 0));

    fN.Dispose();
    brBlack.Dispose();
    brLY.Dispose();
    brGR.Dispose();
    brRED.Dispose();
    penBlack.Dispose();
    penSn.Dispose();
    penGr.Dispose();
    g.Dispose();
    picGr.Dispose();
}

Если изменить размеры формы просмотра, то количество цифр в строках для 1 и 3 режима может не соответствовать ширине области рисования. В методе pictureBox1_SizeChanged перезаполним строки массива цифрами с учетом новой ширины. Код ниже :

private void pictureBox1_SizeChanged(object sender, EventArgs e)
{
    Random rnd = new Random((int)(DateTime.Now.Ticks % 10000000));
    ArrayList al = new ArrayList();
    int i, j, l_str = (int)(pictureBox1.Width / 13) + 30;
    if (cfg.Mode == 1)
    {
         StringBuilder sb = new StringBuilder();
         for (i = 0; i < 10000; i++)
         {
              string s = nn[i].ToString();
              for (j = 0; j < s.Length; j++)
              {
                   if (rnd.Next(0, 20) > 10) sb.Append(" " + s.Substring(j, 1));
                   else sb.Append(s.Substring(j, 1));
              }
              if (sb.Length > l_str)
              {
                   al.Add(sb.ToString()); sb.Clear();
              }
          }
          ar_txt = (string[])al.ToArray(typeof(string));
     }
     if (cfg.Mode == 3)
     {
          StringBuilder sb = new StringBuilder();
          for (i = 0; i < 10000; i++)
          {
               sb.Append(Convert.ToString(nn[i], 2));
               if (sb.Length > l_str)
               {
                    al.Add(sb.ToString()); sb.Clear();
               }
          }
          ar_txt = (string[])al.ToArray(typeof(string));
     }
     Refresh();
}

Файл конфигурации

Чтобы при каждом новом запуске программы ‘Генератор фоновых изображений’ не задавать все настройки снова будем их хранить в файле конфигурации. Создадим для этого отдельный статический класс с двумя методами Load и Save соответственно и, собственно, класс с параметрами конфигурации. Код этих классов приведён ниже :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;

namespace GenBG
{
    [Serializable]
    public class AppCfg
    {
        public Color Col_bg { get; set; }
        public Color Col_sim { get; set; }
        public Color Col1 { get; set; }
        public Color Col2 { get; set; }
        public bool Grad { get; set; }
        public string NameFile { get; set; }
        public int Mode { get; set; }
    }

    public static class SaveLoad //Создание статичного класса позволит использовать методы без объявления его экземпляров
    {
        public static string pathCfg = Application.StartupPath + "/GenBG.cfg"; //Путь к сохранению файла конфигурации. Вы можете использовать любое расширение
        private static BinaryFormatter formatter = new BinaryFormatter(); //Создание сериализатора 

        public static void SaveCfg(AppCfg cfg, string nam = "GenBG.cfg")
        {
            string file_path = Application.StartupPath + "/" + nam;
            //MessageBox.Show(file_path);

            FileStream fs = new FileStream(file_path, FileMode.Create);

            formatter.Serialize(fs, cfg);

            fs.Close();
        }

        public static AppCfg LoadCfg(string file_path)
        {
            if (File.Exists(file_path))
            {//Проверка существования файла сохранения
                FileStream fs = new FileStream(file_path, FileMode.Open); //Открытие потока

                AppCfg cfg = formatter.Deserialize(fs) as AppCfg; //Получение данных

                fs.Close(); //Закрытие потока

                return cfg; //Возвращение данных
            }
            else
            {
                return new AppCfg();
            }
        }
    }
}

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

рис. 5 Набор текста со сдвигом вверх

В качестве текстового файла я выбрал текст класса главного окна приложения. При загрузке формы этот текст был считан и сохранён как массив строк. Будем выводить текст по одной букве при каждой перерисовке экрана, которая происходит по каждому окончанию счёта таймера. Когда весь экран заполнится, будем сдвигать строки вверх. Для управления выводом используем переменные f_str (индекс в массиве строк первой строки), last_str (… последней строки), n_str (количество выводимых строк), nr (число уже выведенных строк), cur_pos (номер буквы в последней строке). Реализация описанного алгоритма в коде ниже :

switch (cfg.Mode)
{
    case 0: //  Набор текста
        //ind_txt++; ind_txt %= txt.Length;
        n_str = (last_str - f_str + ar_txt.Length) % ar_txt.Length;
        for (int k = 0; k < n_str; k++, nr++)
        {
            if (f_str == last_str) break;
            p1.X = 0; p1.Y = nr * fN.Height;
            g.DrawString(ar_txt[(f_str + k) % ar_txt.Length], fN, lgb_sim, p1);
        }
        p1.X = 0; p1.Y = nr * fN.Height;
        if (cur_pos + 1 < ar_txt[last_str].Length)
        {
            cur_pos++; g.DrawString(ar_txt[last_str].Substring(0, cur_pos), fN, lgb_sim, p1);
        }
        else
        {
             g.DrawString(ar_txt[last_str], fN, lgb_sim, p1); cur_pos = 0;
             last_str++; last_str %= ar_txt.Length;
             if ((last_str - f_str + ar_txt.Length) % ar_txt.Length > pictureBox1.Height / fN.Height) f_str++;
        }
        break;

Падающие цифры

При загрузке формы массив чисел заполняется случайными значениями int. Эти числа преобразуются в строки и посимвольно добавляются в строку длиной в ширину экрана. При этом также случайным образом между цифр вставляются пробелы. Готовая длинная строка добавляется в массив строк для вывода.

рис. 6 Цифры падают вниз

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

case 1: //  Падающие цифры
    last_str++; last_str %= ar_txt.Length; 
    n_str = (last_str - f_str + ar_txt.Length) % ar_txt.Length;
    for (int i = 0; i < n_str; i++)
    {
        ss = Convert.ToString(nn[i], 2);
        p1.Y = (n_str - 1 - i) * fN.Height;
        g.DrawString(ar_txt[(i + f_str) % ar_txt.Length], fN, lgb_sim, p1);
        if ((last_str - f_str + ar_txt.Length) % ar_txt.Length > pictureBox1.Height / fN.Height) f_str++;
     }
     break;

Много эллипсов и прямоугольников

При загрузке формы сгенерируем массив из 10000 случайных чисел в диапазоне 0-800. Каждые 4 числа будем использовать для формирования цвета фигуры, координат прямоугольника её расположения на экране и выбора типа фигуры.

рис. 7 Случайные геометрические фигуры

Фигуры выводим последовательно, увеличивая на одну по окончании каждого счёта таймера. Тип фигуры определяет 1 число четвёрки. Для цвета используем остальные 3 приведя их в диапазон 0-255. А в качестве координат прямоугольника нужны все 4 числа. Далее в зависимости от 1 числа рисуем контур эллипса или прямоугольника, или их вариант с заливкой сформированным цветом. Код, реализующий описанный алгоритм ниже :

case 2: //  Случайные фигуры
    ind_txt += 4; ind_txt %= 10000;
    for (int i = 0; i < ind_txt; i += 4)
    {
        Color col = Color.FromArgb(nn[i + 1] % 256, nn[i + 2] % 256, nn[i + 3] % 256);
        Rectangle rc_fig = new Rectangle(nn[i], nn[i + 1], nn[i + 2], nn[i + 3]);
        switch (nn[i] % 4)
        {
            case 0:
                g.DrawRectangle(new Pen(col, 5), rc_fig);
                break;
            case 1:
                g.DrawEllipse(new Pen(col, 5), rc_fig);
                break;
            case 2:
                g.FillRectangle(new SolidBrush(col), rc_fig);
                break;
            case 3:
                g.FillEllipse(new SolidBrush(col), rc_fig);
                break;
         }
     }                        
     break;

Меняющиеся нули и единицы

При загрузке формы из массива случайных чисел формируем массив строк в двоичном коде. Строки выводим до заполнения всего экрана.

рис. 8 Стоки нулей и единиц

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

case 3: //  Меняющиеся цифры
    ind_txt++; ind_txt %= ar_txt.Length;
    for (int i = 0; i < pictureBox1.Height / fN.Height; i++, nr++)
    {
        p1.Y = nr * fN.Height;
        g.DrawString(ar_txt[(i + ind_txt) % ar_txt.Length], fN, lgb_sim, p1);
    }
    break;

Имитация графиков и диаграмм

Для вывода графиков используем тот же массив случайных чисел, что и для графических фигур (режим 2). За одну перерисовку будем выводить 25 вариантов, чтобы все 4 окна области рисования были заполнены. Для генерации используем массивы по 20 чисел, начало которых смещается по каждому пересчёту таймера.

рис. 9 Случайные диаграммы и графики

Первое число используем для определения четверти рисования. 2-4 число используем для формирования двух взаимно противоположных цветов фона и линий (столбцов). Черным пером нарисуем координатные оси в выбранной четверти. На основе 20 значений также в выбранной четверти нарисуем либо график из отрезков, либо столбцы диаграммы. Реализация ниже :

case 4: //  Диаграммы и графики
    ind_txt += 20; ind_txt %= 10000;
    if (ind_txt < 100) ind_txt += 100;
    for (int i = ind_txt - 100; i < ind_txt; i += 4)
    {
        Color col = Color.FromArgb(nn[i + 1] % 256, nn[i + 2] % 256, nn[i + 3] % 256);
        Color col_b = Color.FromArgb(255 - nn[i + 1] % 256, 255 - nn[i + 2] % 256, 255 - nn[i + 3] % 256);
        Rectangle rc_dg = new Rectangle(0, 0, pictureBox1.Width / 2, pictureBox1.Height / 2);
        Rectangle rc_fig = new Rectangle(nn[i], nn[i + 1], nn[i + 2], nn[i + 3]);
        switch (nn[i] / 200)
        {
            case 0:
                break;
            case 1:
                rc_dg.X = pictureBox1.Width / 2;
                break;
            case 2:
                rc_dg.Y = pictureBox1.Height / 2;
                break;
            case 3:
                rc_dg.X = pictureBox1.Width / 2; rc_dg.Y = pictureBox1.Height / 2;
                break;
         }
         g.FillRectangle(new SolidBrush(col_b), rc_dg);
         g.DrawLine(penBlack, rc_dg.X + 5, rc_dg.Y + 5, rc_dg.X + 5, rc_dg.Y + rc_dg.Height - 5);
         g.DrawLine(penBlack, rc_dg.X + 5, rc_dg.Y + rc_dg.Height - 5, rc_dg.X + rc_dg.Width - 5, rc_dg.Y + rc_dg.Height - 5);
         if (nn[i + 1] % 2 == 0)
         {   //  график
             Point pp1 = new Point(rc_dg.X + 5, rc_dg.Y + rc_dg.Height - 5);
             Point pp2 = pp1;
             for (int j = 0; j < 20; j++)
             {
                  pp2.X = ((j + 1) * rc_dg.Width / 21) + rc_dg.X + 5; pp2.Y = nn[i + j] % (rc_dg.Height - 15) + rc_dg.Y + 5;
                  g.DrawLine(new Pen(col, 3), pp1, pp2);
                  pp1 = pp2;
             }
         }
         else
         {   //  диаграмма столбики
             Point pp1 = new Point(rc_dg.X + 5, rc_dg.Y + rc_dg.Height - 5);
             Point pp2 = pp1;
             for (int j = 0; j < 20; j++)
             {
                 pp2.X = ((j + 1) * rc_dg.Width / 21) + rc_dg.X + 5; pp2.Y = nn[i + j] % (rc_dg.Height - 15) + rc_dg.Y + 5;
                 g.FillRectangle(new SolidBrush(col), pp1.X + 3, pp2.Y, rc_dg.Width / 21, rc_dg.Height - pp2.Y - 10);
                 pp1 = pp2;
             }
         }
     }
     break;

Итоги

Настала пора испытать наш ‘Генератор фоновых изображений’. Компилируем и запускаем приложение. Выбираем режим работы. Задаём цвета фона и символов в ColorDialog. В случае градиентных цветов не забываем про вторые цвета для кистей фона и символов. Приложение отображает выбранные цвета и градиентные варианты кистей. Для набора текста нужно выбрать любой текстовый файл, желательно, с большим числом строк чтобы весь экран заполнить.

рис. 10 Выбор цвета

Далее нажимаем кнопку ‘Просмотреть’ и в форме просмотра кликаем для начала отображения движущихся изображений. Форму можно перевести в полноэкранный режим. Как использовать полученные меняющиеся изображения? Можно ‘программой захвата видео’ снять интересующую область экрана, а можно камерой снять и человека за компьютером с таким экраном. На мой взгляд получилась познавательная программа с точки зрения использования графики.

Архив с программой можно скачать по ссылке ниже :

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

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