Итак, камера… Windows Mobile версии 5 и выше предоставляет API для работы с камерой мобильного телефона. Что значит “для работы”? А значит это то, что кроме возможности получения изображения, которая будет рассмотрена в этом посте, предоставляемое API позволяет, также, осуществить захват видео любого формата, поддерживаемого Windows Media Player. Все эти радости жизни доступны посредством функции

HRESULT SHCameraCapture (PSHCAMERACAPTURE pshcc);


Документация по функции SHCameraCapture доступна в MSDN здесь, а описание структуры SHCAMERACAPTURE, которая передается в параметр функции, доступно здесь.
А сейчас мы рассмотрим небольшой пример использования выше указанной функции для получения изображения в формате JPG с камеры мобильного устройства. Ниже представлен код функции, которая отображает диалог для работы с камерой, который позволяет сохранить изображение с камеры в файл. После успешного заврешения работы диалога параметр result будет содержать путь к файлу с изображением.

static bool DoCaptureImage(LPCTSTR captureDir, LPCTSTR defaultFileName, 
						   LPTSTR result, DWORD resultSize, HWND parent)
{
	do
	{
		HRESULT         hResult;
		SHCAMERACAPTURE shcc;

		// Заполняем поля структуры SHCAMERACAPTURE.
		ZeroMemory(&shcc, sizeof(shcc));
		shcc.cbSize				= sizeof(shcc);
		shcc.hwndOwner			= parent;
		shcc.pszInitialDir		= captureDir;
		shcc.pszDefaultFileName	= defaultFileName;
		shcc.pszTitle			= TEXT("Capture Image");
		shcc.nResolutionWidth	= 0;
		shcc.nResolutionHeight	= 0;
		shcc.nVideoTimeLimit	= 0;
		shcc.Mode				= CAMERACAPTURE_MODE_STILL;
		shcc.StillQuality		= CAMERACAPTURE_STILLQUALITY_HIGH;

		// Отображаем диалог для работы с камерой
		hResult = SHCameraCapture(&shcc);
		if(hResult != S_OK) break;
		StringCchCopy(result, resultSize, shcc.szFile);
		return true;
	}
	while(false);
	return false;
}

Идем дальше… Теперь рассмотрим, как все описанное выше можно поиспользовать с wxWidgets и получить изображение в виде переменной wxImage:

static wxImage CaptureImage(WXWidget parent = NULL)
{
	do
	{
		// Создаем временное имя файла
		wxString tempFileName = wxFileName::CreateTempFileName(wxT("capture_"));
		// Т.к. wxWidgets по умолчанию добавляет расширение .tmp, 
		// а нам надо бы .jpg, то добавляем расширение руками
		tempFileName += wxT(".jpg");

		// Копируем путь к каталогу с временным файлом в буфер 
		TCHAR captureDir[MAX_PATH];
		StringCchCopy(captureDir, MAX_PATH, wxPathOnly(tempFileName).GetData());

		// Копируем имя файла в буфер
		TCHAR defaultFileName[MAX_PATH];
		StringCchCopy(defaultFileName, MAX_PATH, wxFileName(tempFileName).GetFullName());

		TCHAR fileNameBuffer[MAX_PATH];

		// Если ошибка то выходим...
		if (!DoCaptureImage(captureDir, defaultFileName, 
			fileNameBuffer, MAX_PATH, (HWND)parent)) break;
		// Получаем имя файла в переменную wxString
		wxString resultFileName(fileNameBuffer);
		// Загружаем отснятое изображение
		wxImage result(resultFileName, wxBITMAP_TYPE_ANY);
		// Удаляем временный файл
		if(wxFileExists(resultFileName)) wxRemoveFile(resultFileName);
		// Если изображение было загружено неудачно, то возвращаем false
		if(!result.IsOk()) break;
		// Если все нормально, то возвращаем true
		return result;
	}
	while(false);
	return wxNullImage;
}

Вот, такой нехитрый финт ушами позволяет нам получить изображение в виде переменной wxImage, которую можно использовать в GUI-приложении, например как-то так:

void wxCameraTestWMMainFrame::OnOPENClick( wxCommandEvent& event )
{
	wxImage capturedImage = 
		wxWMCameraCapture::CaptureImage(GetHandle());
	if(capturedImage.IsOk())
	{
		double controlScale = 
			(double)m_StaticBitmap->GetClientSize().GetHeight()/
			(double)m_StaticBitmap->GetClientSize().GetWidth();
		double imageScale = 
			(double)capturedImage.GetHeight() / 
			(double)capturedImage.GetWidth();
		double zoomLevel(1.0);
		if(imageScale > controlScale)
		{
			zoomLevel = (double)m_StaticBitmap->GetSize().GetHeight() / (double)capturedImage.GetHeight();
		}
		else
		{
			zoomLevel = (double)m_StaticBitmap->GetSize().GetWidth() / (double)capturedImage.GetWidth();
		}
		wxImage scaledImage = capturedImage.Scale(capturedImage.GetWidth() * zoomLevel,
			capturedImage.GetHeight() * zoomLevel);
		m_StaticBitmap->SetBitmap(wxBitmap(scaledImage));
	}
}

The конец.

Исходный код примера и проект для Visual Studio 2008 можно скачать здесь:
Пример получения изображения с камеры под Windows Mobile

Previous ArticleNext Article
Технический директор IT-Dimension, компании-разработчика кросс-платформенного программного обеспечения

This post has 4 Comments

4
  1. Прикольно, полезно – но не компилится .. уже неделю на это убил… склоняюсь к мысли что будет быстрее переписать под C#….

    Есть ли еще порталы \ статьи по работе с камерой (С++\С#)? а то что-то инфы как кот наплакал…

  2. Мы уже с тобой обсуждали это в аське. проблема не в примере, а в том что ты что-то не так делаешь. билдлог давай на паст.орг.ру и ссыль сюда

  3. Очень нужна помощь в написании программы, которая делает фотоснимок. Если кто-нибудь прочтёт мой коментарий в этой старой статье и сможет помочь, напишите плз в icq: 330-112-063

Leave a Reply

Your email address will not be published. Required fields are marked *

C.

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 миллисекунд (проверялось на разнообразных устройствах). Для создания приложения в таком стиле, безусловно, стандартные контролы не подойдут, но а кто обещал, что будет легко? В любом случае, для создания неописуемой красоты без собственного фреймворка рендеринга графических элементов не обойтись.

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