Получение указателей на буферы данных (C++/CX)
В среде выполнения Windows интерфейс Windows::Storage::Streams::IBuffer предоставляет не зависящий от языка и основанный на потоках механизм доступа к буферам данных. В C++ можно получить необработанный указатель на базовый массив байтов с помощью интерфейса IBufferByteAccess библиотеки среды выполнения Windows, определенный в файле robuffer.h. Благодаря такому подходу можно изменить массив байтов на месте без создания лишний копий данных.
На следующей схеме показан элемент изображения XAML, источником которого является Windows::UI::Xaml::Media::Imaging WriteableBitmap. Клиентские приложения, написанные на любом языке, могут передавать ссылку на WriteableBitmap
в код C++, а затем код C++ может использовать эту ссылку для обращения к соответствующему буферу. В приложении универсальная платформа Windows, написанном на C++, можно использовать функцию в следующем примере непосредственно в исходном коде, не упаковывая его в компонент среда выполнения Windows.
GetPointerToPixelData
Следующий метод принимает Windows::Storage::Streams::IBuffer и возвращает необработанный указатель на соответствующий массив байтов. Чтобы вызвать функцию, передайте свойство WriteableBitmap::PixelBuffer .
#include <wrl.h>
#include <robuffer.h>
using namespace Windows::Storage::Streams;
using namespace Microsoft::WRL;
typedef uint8 byte;
// Retrieves the raw pixel data from the provided IBuffer object.
// Warning: The lifetime of the returned buffer is controlled by
// the lifetime of the buffer object that's passed to this method.
// When the buffer has been released, the pointer becomes invalid
// and must not be used.
byte* Class1::GetPointerToPixelData(IBuffer^ pixelBuffer, unsigned int *length)
{
if (length != nullptr)
{
*length = pixelBuffer ->Length;
}
// Query the IBufferByteAccess interface.
ComPtr<IBufferByteAccess> bufferByteAccess;
reinterpret_cast<IInspectable*>( pixelBuffer)->QueryInterface(IID_PPV_ARGS(&bufferByteAccess));
// Retrieve the buffer data.
byte* pixels = nullptr;
bufferByteAccess->Buffer(&pixels);
return pixels;
}
Полный пример
Ниже показано, как создать приложение универсальная платформа Windows C#, которое передает WriteableBitmap
библиотеку DLL компонента C++ среда выполнения Windows. Код C++ получает указатель на буфер пикселя, выполняющий простое изменение изображения на месте. В качестве альтернативы можно создать клиентское приложение на языке Visual Basic, JavaScript или C++, а не на C#. При использовании C++ нет необходимости в компоненте DLL; можно просто добавить эти методы непосредственно в класс MainPage или в какие-либо другие классы.
Создание клиента
Используйте шаблон проекта пустого приложения для создания приложения универсальная платформа Windows C#.
В файле MainPage.xaml
Используйте это XAML-файл, чтобы заменить элемент
Grid
:<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <StackPanel HorizontalAlignment="Left" Margin="176,110,0,0" VerticalAlignment="Top" Width="932"> <Image x:Name="Pic"/> <Button Content="Process Image" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="47" Click="Button_Click_1"/> </StackPanel> </Grid>
В файле MainPage.xaml.cs
Добавьте следующие объявления пространств имен:
using Windows.Storage; using Windows.Storage.FileProperties; using Windows.UI.Xaml.Media.Imaging; using Windows.Storage.Streams; using Windows.Storage.Pickers;
Добавьте переменную-член
WriteableBitmap
в классMainPage
и присвойте ей имяm_bm
.private WriteableBitmap m_bm;
С помощью следующего кода замените заглушку метода
OnNavigatedTo
. В результате при запуске приложения будет открываться средство выбора файлов. (Обратите внимание, что в сигнатуру функции добавляется ключевое словоasync
).async protected override void OnNavigatedTo(NavigationEventArgs e) { FileOpenPicker openPicker = new FileOpenPicker(); openPicker.ViewMode = PickerViewMode.Thumbnail; openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary; openPicker.FileTypeFilter.Add(".jpg"); openPicker.FileTypeFilter.Add(".jpeg"); openPicker.FileTypeFilter.Add(".png"); StorageFile file = await openPicker.PickSingleFileAsync(); if (file != null) { // Get the size of the image for the WriteableBitmap constructor. ImageProperties props = await file.Properties.GetImagePropertiesAsync(); m_bm = new WriteableBitmap((int)props.Height, (int)props.Width); m_bm.SetSource(await file.OpenReadAsync()); Pic.Source = m_bm; } else { // Handle error... } }
Добавьте обработчик событий для нажатия кнопки. (Так как ссылка на пространство имен
ImageManipCPP
еще не создана, он может быть подчеркнут волнистой линией в окне редактора).async private void Button_Click_1(object sender, RoutedEventArgs e) { ImageManipCPP.Class1 obj = new ImageManipCPP.Class1(); await obj.Negativize(m_bm); Pic.Source = m_bm; }
Создание компонента C++
Добавьте новый компонент среда выполнения Windows C++ в существующее решение и назовите его
ImageManipCPP
. Добавьте ссылку на него в проект C#, щелкнув этот проект правой кнопкой мыши в Обозревателе решений и выбрав Добавить, Ссылка.В файле Class1.h
Добавьте определение
typedef
во вторую строку сразу после#pragma once
.typedef uint8 byte;
Добавьте атрибут
WebHostHidden
перед началом объявленияClass1
.[Windows::Foundation::Metadata::WebHostHidden]
Добавьте следующую сигнатуру открытого метода в
Class1
:Windows::Foundation::IAsyncAction^ Negativize(Windows::UI::Xaml::Media::Imaging::WriteableBitmap^ bm);
Добавьте сигнатуру из метода
GetPointerToPixelData
, показанного в предыдущем фрагменте кода. Убедитесь, что этот метод является закрытым.
В файле Class1.cpp
Добавьте следующие директивы
#include
и объявления пространств имен:#include <ppltasks.h> #include <wrl.h> #include <robuffer.h> using namespace Windows::Storage; using namespace Windows::UI::Xaml::Media::Imaging; using namespace Windows::Storage::Streams; using namespace Microsoft::WRL;
Добавьте реализацию
GetPointerToPixelData
из расположенного выше фрагмента кода.Добавьте реализацию
Negativize
. Этот метод создает эффект, похожий на фотонегатив, инвертируя каждое RGB-значение пикселя. Этот метод сделан асинхронным, поскольку на больших изображениях его выполнение может занять много времени.IAsyncAction^ Class1::Negativize(WriteableBitmap^ bm) { unsigned int length; byte* sourcePixels = GetPointerToPixelData(bm->PixelBuffer, &length); const unsigned int width = bm->PixelWidth; const unsigned int height = bm->PixelHeight; return create_async([this, width, height, sourcePixels] { byte* temp = sourcePixels; for(unsigned int k = 0; k < height; k++) { for (unsigned int i = 0; i < (width * 4); i += 4) { int pos = k * (width * 4) + (i); temp[pos] = ~temp[pos]; temp[pos + 1] = ~temp[pos + 1] / 3; temp[pos + 2] = ~temp[pos + 2] / 2; temp[pos + 3] = ~temp[pos + 3]; } } }); }
Примечание.
Этот метод может выполняться быстрее, если для параллелизации операции используется AMP или библиотека параллельных шаблонов.
Убедитесь, что в папке рисунков есть по крайней мере одно изображение, а затем нажмите клавишу F5, чтобы компилировать и выполнить программу.