Condividi tramite


Recupero di puntatori a buffer di dati (C++/CX)

In Windows Runtime l'interfaccia Windows::Storage::Streams::IBuffer fornisce un modo indipendente dalla lingua e basato sui flussi per accedere ai buffer di dati. In C++ puoi ottenere un puntatore non elaborato alla matrice di byte sottostante utilizzando l'interfaccia IBufferByteAccess della libreria di Windows Runtime definita in robuffer.h. Con questo approccio puoi modificare la matrice di byte sul posto senza creare inutili copie dei dati.

Il diagramma seguente mostra un elemento immagine XAML, la cui origine è Windows::UI::Xaml::Media::Imaging WriteableBitmap. Un'app client scritta in qualsiasi linguaggio può passare un riferimento a WriteableBitmap al codice C++, quindi C++ può utilizzare il riferimento per ottenere il buffer sottostante. In un'app piattaforma UWP (Universal Windows Platform) scritta in C++, puoi usare la funzione nell'esempio seguente direttamente nel codice sorgente senza crearne il pacchetto in un componente Windows Runtime.

Diagramma che mostra un componente di codice che accede direttamente ai dati pixel.

GetPointerToPixelData

Il metodo seguente accetta Windows::Storage::Streams::IBuffer e restituisce un puntatore non elaborato alla matrice di byte sottostante. Per chiamare la funzione, passare una proprietà 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;
}

Esempio completo

I passaggi seguenti illustrano come creare un'app C# piattaforma UWP (Universal Windows Platform) che passa un oggetto WriteableBitmap a una DLL del componente Windows Runtime C++. Il codice C++ ottiene un puntatore al buffer di pixel ed esegue una semplice modifica dell'immagine sul posto. In alternativa, puoi creare l'app client in Visual Basic, in JavaScript o in C++, anziché C#. Se usi C++, non è necessaria la DLL del componente. Puoi semplicemente aggiungere tali metodi direttamente alla classe MainPage o a un'altra classe che definisci.

Creare il client

  1. Usare il modello di progetto App vuota per creare un'app di piattaforma UWP (Universal Windows Platform) C#.

  2. In MainPage.xaml

    • Usa questo codice XAML per sostituire l'elemento 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>
      
  3. In MainPage.xaml.cs

    1. Aggiungi queste dichiarazioni dello spazio dei nomi:

      using Windows.Storage;
      using Windows.Storage.FileProperties;
      using Windows.UI.Xaml.Media.Imaging;
      using Windows.Storage.Streams;
      using Windows.Storage.Pickers;
      
    2. Aggiungi una variabile membro WriteableBitmap alla classe MainPage e assegnale il nome m_bm.

      private WriteableBitmap m_bm;
      
    3. Usa il codice seguente per sostituire lo stub del metodo OnNavigatedTo . All'avvio dell'app viene visualizzato il selettore di file. La parola chiave async viene aggiunta alla firma della funzione.

      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...
          }
      }
      
    4. Aggiungi il gestore eventi per il clic del pulsante. Poiché il riferimento allo spazio dei nomi ImageManipCPP non è ancora stato creato, potrebbe avere una sottolineatura ondulata nella finestra dell'editor.

      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;
      }
      

Creare il componente C++

  1. Aggiungere un nuovo componente Windows Runtime C++ alla soluzione esistente e denominarlo ImageManipCPP. Aggiungi un riferimento al progetto C# facendo clic con il pulsante destro del mouse sul progetto in Esplora soluzioni e scegliendo Aggiungi, Riferimento.

  2. In Class1.h

    1. Aggiungi questo typedef nella seconda riga, subito dopo #pragma once:

      typedef uint8 byte;
      
    2. Aggiungi l'attributo WebHostHidden sopra l'inizio della dichiarazione di Class1 .

      [Windows::Foundation::Metadata::WebHostHidden]
      
    3. Aggiungi questa firma del metodo pubblico a Class1:

      Windows::Foundation::IAsyncAction^ Negativize(Windows::UI::Xaml::Media::Imaging::WriteableBitmap^ bm);
      
    4. Aggiungi la firma del metodo GetPointerToPixelData indicata nel frammento di codice precedente. Assicurati che il metodo sia privato.

  3. In Class1.cpp

    1. Aggiungi le direttive #include e le dichiarazioni dello spazio dei nomi:

      #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;
      
    2. Aggiungi l'implementazione di GetPointerToPixelData dal frammento di codice precedente.

    3. Aggiungi l'implementazione di Negativize. Questo metodo crea un effetto simile al negativo di una pellicola invertendo ogni valore RGB nel pixel. Rendiamo il metodo asincrono in quanto per le immagini più grandi il completamento può richiedere una quantità di tempo percettibile.

      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];
                  }
              }
          });
      
      }
      

      Nota

      Questo metodo può essere eseguito più velocemente se usi AMP o la libreria PPL per parallelizzare l'operazione.

  4. Verifica di avere almeno un'immagine nella cartella delle immagini, quindi premi F5 per compilare ed eseguire il programma.