Mar
17

Compact Framework: Грани прозрачности

Еще одна интересная статья от Андрея Коновалова. В статье рассмотрены особенности реализации отрисовки изображений с прозрачностью при использовании .NET Compact Framework.

Вступление

К большому сожалению разработчиков, Compact Framework, да и native-функции тоже, не поддерживают альфа-канал с разной прозрачностью у индивидуальных пикселей. Это означает, что нет возможности создавать красоту неописуемую с плавными переходами между изображениями. Однако, что же делать, если хочется иметь хотя бы подобие “полного” альфа-канала, а именно, выводить полупрозрачные изображения, у которых есть ещё и полностью прозрачные участки?

Рассмотрим два способа вывода изображений с прозрачностью.

Способ №1. Фиксированый цвет является прозрачным

public static void DrawImageTransparent(Graphics g, 
  Bitmap b, Point location, Color transColor)
{
  if (b == null || g == null)
    return;

  ImageAttributes attrib = new ImageAttributes();
  attrib.SetColorKey(transColor, transColor);

  Rectangle destRect = new Rectangle(location.X, location.Y, b.Width, b.Height);
 
  g.DrawImage(b, destRect, 0, 0, b.Width, b.Height, GraphicsUnit.Pixel, attrib);
}

Стоит заметить, что только эта хитрая разновидность DrawImage позволяет выводить изображение с указанным ColorKey, по которому определяется, какие пиксели не рисовать. Шикарный набор параметров, не находите? :) Куда рисовать, мы задаём через Rectange, а откуда — через 4 параметра. Ну это я так, лирическое отступление в сторону Microsoft.

Собственно, именно DrawImageTransparent и есть основной способ рисования изображений с прозрачными пикселями. Однако минус этого способа очевиден, состояния прозрачности всего два: полностью прозрачно и совсем непрозрачно.

Пример:

Compact Framework - Грани прозрачности
На самом деле, вполне неплохо, можно на этом и остановиться. Но хочется-то большего :)

Способ №2. У всего изображения фиксированный коэффициент непрозрачности

В этом случае без DllImport уже не обойтись, приготовим всё, что для этого необходимо:

public struct BlendFunction
{
  public byte BlendOp;
  public byte BlendFlags;
  public byte SourceConstantAlpha;
  public byte AlphaFormat;
}

public enum BlendOperation : byte
{
  AC_SRC_OVER = 0x00
}

public enum BlendFlags : byte
{
  Zero = 0x00
}

public enum SourceConstantAlpha : byte
{
  Transparent = 0x00,
  Opaque = 0xFF
}

public enum AlphaFormat : byte
{
  AC_SRC_ALPHA = 0x01
}

public class PlatformAPI
{
  [DllImport("coredll.dll")]
  extern public static Int32 AlphaBlend(IntPtr hdcDest, 
    Int32 xDest, Int32 yDest, Int32 cxDest, Int32 cyDest, 
    IntPtr hdcSrc, Int32 xSrc, Int32 ySrc, Int32 cxSrc, 
    Int32 cySrc, BlendFunction blendFunction);        
}

Как видно, обрезано всё, что только можно обрезать — в enum-ах по одному параметру и т.д. Но тем не менее, продолжаем. Собственно, наша функция:

public static void DrawAlpha(Graphics g, Bitmap b, Point location, byte opacity)
{
  if (b == null || g == null)
    return;

  using (Graphics gxSrc = Graphics.FromImage(g))
  {
    IntPtr hdcDst = g.GetHdc();
    IntPtr hdcSrc = gxSrc.GetHdc();
    BlendFunction blendFunction = new BlendFunction();
    blendFunction.BlendOp = (byte)BlendOperation.AC_SRC_OVER;
    blendFunction.BlendFlags = (byte)BlendFlags.Zero;
    blendFunction.SourceConstantAlpha = opacity;
    blendFunction.AlphaFormat = (byte)0;    
    PlatformAPI.AlphaBlend(hdcDst, location.X, location.Y, 
      b.Width, b.Height, hdcSrc, 0, 0, b.Width, b.Height, blendFunction);
    g.ReleaseHdc(hdcDst);
    gxSrc.ReleaseHdc(hdcSrc);
  }
}

Небольшие комментарии по коду — параметры у BlendFunction нельзя менять, они проставляются единственно возможные. Это обидно, но делать нечего.

Пример:
Compact Framework - грани прозрачности
Жутковато, да? Противные фиолетовые пиксели никуда не делись и тоже стали немного прозрачными :(

Комбинированное использование обоих способов

Вариантов комбинирования у нас, к сожалению, немного. На первый взгляд их совсем нет :) Но есть всё-таки один способ.

Итак, решение следующее. Раз мы не можем одновременно задать ColorKey и вызвать AlphaBlend, будем использовать их по очереди. Сначала нарисуем фон стандартным спосбом без изысков, затем кнопку первым спосбом, а в конце… вторым спосбом нарисуем поверх фон с небольшим коэффициентом непрозрачности!

g.DrawImage(background, 0, 0);
DrawImageTransparent(g, button, new Point(10, 10), Color.FromArgb(255, 0, 255));
DrawAlpha(g, background, new Point(0, 0), 75);

Результат:
Compact Framework - Грани прозрачности

Описанный выше способ вполне жизнеспособен. Я им пользуюсь и вполне удовлетворён скоростью работы — на отрисовку всех элементов интерфейса в подобном стиле уходит в среднем от 60 до 80 миллисекунд (проверялось на разнообразных устройствах). Для создания приложения в таком стиле, безусловно, стандартные контролы не подойдут, но а кто обещал, что будет легко? В любом случае, для создания неописуемой красоты без собственного фреймворка рендеринга графических элементов не обойтись.

Оригинал статьи на Хабре.

Еще интересные посты о программировании для мобильных устройств:

2 Comments

Make A Comment
  • Remy Said:

    а можете уточнить пожалуйста что имело ввиду под этими словами “собственного фреймворка рендеринга графических элементов”?? что для красивого интерфейса не стоит использовать написаные вручную элементы управления? то есть лучше иметь один элемент управления и в нем все отрисовывать ручками?то есть не используя кучу специальных классических пользовательских элементов управления?

  • T-Rex Said:

    Я думаю, здесь имелось в виду owner-drawn’ая версия каждого контрола. Хотя в принципе вариант когда одним контролом эмулируется работа всего интерфейса пользователя тоже не так и плох, для простых приложений это было бы нормально. В OpenGL-играх ведь тоже те же меню рендерятся в одной сцене на одном контроле.

Comments RSS Feed   TrackBack URL

Leave a comment

Please leave these two fields as-is:
Собираетесь красить волосы - мелирование. ; Отели Индонезии - отдых сингапур бали. Отдых на частных виллах Бали. ; Вся Чехия и Прага зима-весна - экскурсионные туры по европе. ; Правильно выбрать усилители автомобильные самому достаточно тяжело .

top