netlib.narod.ru< Назад | Оглавление | Далее >

Рисование на изображении

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

Для рисования на изображении нужно получить ссылающийся на него объект Graphics. Это позволяет сделать статический метод класса Graphics:


Статические методы Graphics (выборочно)



Graphics Graphics.FromImage(Image image)


Например, следующая инструкция получает объект Graphics с именем grfxImage, основанный на объекте Image с именем image:

  Graphics grfxImage = Graphics.FromImage{image);

Закончив работу с таким объектом Graphics, вызовите метод Dispose чтобы удалить его.

Метод Graphics.FromImage работает не со всеми форматами. Если значение свойства PixelFormat изображения — Format1bppIndexed, Format4bppIndexed, Format8bppIndexed, Format16bppGrayScale или Formatl6Argb1555, он генерирует исключение. Это ограничение может пригодиться. Например, вы получили объект Graphics, ссылающийся на индексированное изображение, и пытаетесь нарисовать что-либо с использованием цвета, отсутствующего в палитре изображения. Аналогичная проблема возникнет, если вы получите объект Graphics для картинки в градациях серого или картинки с 1 битом прозрачности.

Получить объект Graphics для изображения, загруженного из старого метафайла Windows (WMF), или расширенного метафайла Windows (EMF) нельзя. Кое-что о рисовании на метафайлах я расскажу в главе 23.

Объект Graphics, полученный методом Graphics.FromImage, можно использовать так же, как и объект Graphics для дисплея или принтера. Просмотрев значения свойств DpiX и DpiY, вы обнаружите, что они равны значениям свойств HorizontalResolution и VerticalResolution объекта Image. По умолчанию при преобразовании страницы параметр PageUnit имеет значение GraphicsUnit.Display, а параметр PageScale равен 1, что для изображений аналогично значению параметра PageUnit, равному GraphicsUnit.Pixels. Значение свойства VisibleClipBounds данного объекта Graphics по умолчанию равно высоте и ширине изображения в пикселах.

Для объекта Graphics можно задать другие параметры преобразования страницы. При изменении значений свойств PageUnit и PageScale свойство VisibleClipBounds будет указывать размеры изображения в единицах измерения страницы. Так, если переопределить значения свойств PageUnit и PageScale

  grfxImage.PageUnit  = GraphicsUnit.Inch;
  grfxImage.PageScale = 1;

свойство VisibleClipBounds будет указывать размер в дюймах. Этот размер также можно рассчитать, разделив пиксельный размер изображения на значения свойств HorizontalResolution и VerticalResolution.

Чуть позже мы будем выводить текст на поверхности растрового изображения. Возникает вопрос: какой шрифт использовать? Удовлетворяет ли нашим требованиям установленное по умолчанию значение свойства Font формы?

Устанавливаемое по умолчанию значение свойства Font формы  — это шрифт размером 8 пт. Если вывести текст на растровом изображении этим шрифтом и затем отобразить изображение в метрическом (а не пиксельном) размере, текст будет такого же размера, как при выводе в самой клиентской области. Но если разрешение картинки меньше разрешения дисплея, текст будет выглядеть иначе. Контуры текста на изображении будут более грубыми, чем контуры текста, выводимого в окне.

Рассмотрим пример. Разрешение используемых в этой главе растровых изображений — 72 dpi, т.е. один пиксел на пункт. Это значит, что высота шрифта размером 8 пт при таком разрешении составит около 8 пикселов. Если применить установленное по умолчанию свойство Font формы и вызвать:

  Font.GetHeight(72)

или воспользоваться объектом Graphics, созданным на основе одного из прилагающихся к главе растровых изображений, и вызвать:

  Font.GetHeight(grfxImage)

вы получите межстрочный интервал в 8,83 пиксела, а значит, высота символа составляет около 8 пикселов.

8 пикселов явно мало для вывода символов с нормальными очертаниями. И все же при использовании установленного по умолчанию свойства Font формы на изображении с разрешением 72 dpi метод DrawString не сможет выводить текст высотой более 8 пикселов. Если разрешение дисплея превышает 72 dpi и изображение выводится в пиксельном размере, текст на нем будет очень мелким. При выводе картинки в метрическом размере текст будет крупнее. На дисплее с разрешением 96 dpi изображение (а значит, и текст на нем) увеличатся приблизительно на треть, а на дисплее с разрешением 120 dpi — на две трети. Таким образом, эти 8 пикселов растягиваются до обычного размера шрифта формы по умолчанию.

Рассмотрим программу.

DrawOnImage.cs

  //--------------------------------------------
  // DrawOnImage.cs (C) 2001 by Charles Petzold
  //--------------------------------------------
  using System;
  using System.Drawing;
  using System.Windows.Forms;

  class DrawOnImage: PrintableForm
  {
      Image  image;
      string str = "Apollo11";

      public new static void Main()
      {
          Application.Run(new DrawOnImage());
      }
      public DrawOnImage()
      {
          Text  = "Draw on Image";
          image = Image.FromFile("..\\..\\..\\Apollo11FullColor.jpg");

          Graphics grfxImage  = Graphics.FromImage(image);

          grfxImage.PageUnit  = GraphicsUnit.Inch;
          grfxImage.PageScale = 1;

          SizeF sizef = grfxImage.MeasureString(str, Font);

          grfxImage.DrawString(str, Font, Brushes.White, 
                         grfxImage.VisibleClipBounds.Width - sizef.Width, 0);

          grfxImage.Dispose();
      }
      protected override void DoPage(Graphics grfx, Color clr, int cx, int cy)
      {
          grfx.PageUnit = GraphicsUnit.Pixel;
          grfx.DrawImage(image, 0, 0);
          grfx.DrawString(str, Font, new SolidBrush(clr),
                    grfx.DpiX * image.Width / image.HorizontalResolution, 0);
      }
  }

Ради интереса я изменил параметры преобразования страницы для объекта Graphics, связанного с изображением, и теперь единицей измерения при выводе является дюйм. Я определяю размеры строки методом MeasureString и затем задаю координаты в методе DrawString так, что строка выводится в верхнем правом углу изображения. Метод DoPage вызывает для вывода картинки метод DrawImage и затем, для сравнения, также вызывает метод DrawString для вывода той же строки текста справа от изображения (изменение единиц измерения страницы на дюймы позволяет правильно позиционировать текст при выводе на принтер). Вы видите, что текст на изображении более грубый и несколько искажен:


Рис. 11.11.

Можно ли устранить искажение? Оно вызвано применением шрифта, имеющего фиксированную высоту в 9 пикселов. Решить проблему позволяет шрифт с большим размером в пикселах. Для этого потребуется использовать изображение с разрешением больше экранного или увеличить размер шрифта хотя бы до 12 пикселов. Во втором случае текст будет крупнее, но хоть читаемым.

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

DrawOnPixelSizeImage.cs

  //-----------------------------------------------------
  // DrawOnPixelSizeImage.cs (C) 2001 by Charles Petzold
  //-----------------------------------------------------
  using System;
  using System.Drawing;
  using System.Windows.Forms;

  class DrawOnPixelSizeImage: PrintableForm
  {
      Image  image;
      string str = "Apollo11";

      public new static void Main()
      {
          Application.Run(new DrawOnPixelSizeImage());
      }
      public DrawOnPixelSizeImage()
      {
          Text  = "Draw on Pixel-Size Image";
          image = Image.FromFile("..\\..\\..\\Apollo11FullColor.jpg");

          Graphics grfxImage  = Graphics.FromImage(image);
          Graphics grfxScreen = CreateGraphics();

          Font font = new Font(Font.FontFamily, 
                grfxScreen.DpiY / grfxImage.DpiY * Font.SizeInPoints);

          SizeF sizef = grfxImage.MeasureString(str, font);

          grfxImage.DrawString(str, font, Brushes.White, 
                               image.Width - sizef.Width, 0);
          grfxImage.Dispose();
          grfxScreen.Dispose();
      }
      protected override void DoPage(Graphics grfx, Color clr, int cx, int cy)
      {
          grfx.DrawImage(image, 0, 0, image.Width, image.Height);
          grfx.DrawString(str, Font, new SolidBrush(clr), image.Width, 0);
      }
  }

Конструктор создает новый объект Font (с именем font), идентичный шрифту по умолчанию, но с размерами в пунктах, масштабированный с применением коэффициента равного отношению экранного разрешения к разрешению изображения. Этот шрифт используется в вызовах методов MeasureString и DrawString. Так как я не задавал нестандартных параметров преобразования страницы, для позиционирования строки в вызове метода DrawString я могу использовать ширину картинки в пикселах. Выводимый на изображении шрифт визуально имеет такой же размер, что и выводимый в клиентской области.


Рис. 11.12.

Теперь два шрифта выглядят одинаково, хотя их размер в пунктах различен. Они кажутся идентичными по размеру, поскольку выводятся на двух разных поверхностях (картинка и клиентская область) с разными разрешениями. Так как в случае с изображением я задавал размер шрифта, основываясь на разрешении изображения, при печати на принтере шрифты будут отличаться друг от друга на столько, на сколько виртуальное разрешение принтера в 100 dpi отличается от экранного.

Я не упомянул о том, что в вызове DrawString для рисования на изображении применяется метод Brushes.White. Это то, что нужно (конечно, с учетом черного фона), однако для рисования вы должны заранее знать, что представляет собой изображение!

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


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz