neue Funktionen in Windows Phone 7.5 – Teil 2 (Teil 2)
Kommentare

Wichtig ist hier der Aufruf von BeginInvoke. Das UI von .NETCF-Anwendungen darf nur aus dem User Interface Thread heraus aufgerufen werden. Leider laufen die Callbacks des Kamera-Objekts nicht am UI-Thread.

Wichtig ist hier der Aufruf von BeginInvoke. Das UI von .NETCF-Anwendungen darf nur aus dem User Interface Thread heraus aufgerufen werden. Leider laufen die Callbacks des Kamera-Objekts nicht am UI-Thread. Daher muss auf BeginInvoke ausgewichen werden, wenn man auf die Steuerelemente der Page zugreifen will. Aus Vollständigkeitsgründen hier noch der Code, der auf das volle Durchdrücken des Hardwarekameraknopfs reagiert:

private void OnButtonFullPress(object sender, EventArgs e)
{
  cam.CaptureImage();
}  

In jedem Fall macht sich die Kamera des Telefons nach dem Aufruf von CaptureImage an die Arbeit und wird nach einiger Zeit zwei Bilder produzieren, das volle Bild und zusätzlich einen Thumbnail. Dieser dient der Medienanwendung zur „schnelleren Anzeige“ der im Telefon gespeicherten Bilder, sofern er denn mitgespeichert wird. Das Abspeichern des eigentlichen Bilds erfolgt mit dem Code in Listing 4. Im ersten Schritt wird hier der Dateiname der zu speichernden Datei ermittelt. Da die meisten Telefone und Digitalkameras ihre Bilder durchnummerieren, wird Selbiges auch in der Beispielanwendung gemacht. Danach wird MediaLibrary aufgerufen und das neue Bild zur Bilderbibliothek des Telefons hinzugefügt. Ist auch diese Hürde genommen, wird das Bild als .jpg-Datei in den Isolated Storage der eigenen Anwendung gespeichert. Der Code zum Verarbeiten des in der Medienanwendung gezeigten Thumbnails ist größtenteils analog. Allerdings entfällt der Aufruf von MediaLibrary, und dem Dateinamen wird das Suffix _th hinzugefügt.

Listing 4

string fileName = savedCounter + ".jpg";
  Deployment.Current.Dispatcher.BeginInvoke(delegate()
  {
    //Save picture to device media library
    MediaLibrary library = new MediaLibrary();
    library.SavePicture(fileName, e.ImageStream);

    //Save it as a JPEG to isolated storage
    BitmapImage bmpImage = new BitmapImage();
    bmpImage.CreateOptions = BitmapCreateOptions.None;
    bmpImage.SetSource(e.ImageStream);

    using (IsolatedStorageFile isStore = IsolatedStorageFile.GetUserStoreForApplication())
    {
      using (IsolatedStorageFileStream targetStream = isStore.OpenFile(fileName, FileMode.Create, FileAccess.Write))
      {
        WriteableBitmap bitmap = new WriteableBitmap(bmpImage);
        bitmap.SaveJpeg(targetStream, bitmap.PixelWidth, bitmap.PixelHeight, 0, 100);
      }
    }
  });

}  

Direkter Framezugriff

Das obige Beispiel ermöglicht das Festlegen der Auflösung und des Blitzmodus. Wer direkt in den Bilddatenstrom eingreifen will, muss das gespeicherte Bild nach dem Aufruf der Callbacks bearbeiten. Das ist zwar ein durchaus möglicher Weg, allerdings nicht sonderlich bequem. Angenehmer wäre es, wenn man die eigene Verarbeitung der Bilddaten direkt in den Kameraprozess einbinden könnte. Mango macht das möglich.

Der Kern eines derartigen Systems ist die Frame-Pumpe. Hinter diesem hochtrabenden Namen verbirgt sich eine Routine, die jeden einzelnen eingehenden Frame (möglichst schnell) bearbeitet. Microsoft bietet hierzu die Beispielroutine in Listing 5 an. Hier sind zwei Sachen relevant: a) die Verwendung des Bitmaps wb, b) der Aufruf von ColorToGray. Das Bitmap wb wird dabei als Variable der Page deklariert, die Funktion ColorToGray wendet einen primitiven Graustufenalgorithmus auf jeden Pixel des Bilds an. Mit der Routine in Listing 6 wird die Frame-Pumpe in den Vorschauprozess eingebunden.

Listing 5

//ARGB frame pump
  void PumpARGBFrames()
  {

    int[] ARGBPx = new int[640 * 480];

    try
    {
      PhotoCamera phCam = (PhotoCamera)cam;

      while (pumpARGBFrames)
      {
        captureEvent.WaitOne();

        pauseFramesEvent.WaitOne();
        //Copies the current viewfinder frame into a buffer for further manipulation
        phCam.GetPreviewBufferArgb32(ARGBPx);
        //Conversion to grayscale
        for (int i = 0; i < ARGBPx.Length; i++)
        {
          ARGBPx[i] = ColorToGray(ARGBPx[i]);
        }

        pauseFramesEvent.Reset();
        Deployment.Current.Dispatcher.BeginInvoke(delegate()
        {
          //Copy to WriteableBitmap
          ARGBPx.CopyTo(wb.Pixels, 0);
          wb.Invalidate();

          pauseFramesEvent.Set();
        });
      }

    }

    catch
    {
      this.Dispatcher.BeginInvoke(delegate()
      {

      });

    }
  }  

Listing 6

private void GrayOn_Clicked(object sender, RoutedEventArgs e)
{
  MainImage.Visibility = Visibility.Visible;
  pumpARGBFrames = true;
  ARGBFramesThread = new System.Threading.Thread(PumpARGBFrames);

  wb = new WriteableBitmap(640, 480);
  // Set width and height of MainImage
  MainImage.Width = 640;
  MainImage.Height = 480;
  this.MainImage.Source = wb;
  //Start pump
  ARGBFramesThread.Start();
}  

Als Erstes wird hierbei das Rectangle durch ein Image ersetzt, in das die von der Pumpe angelieferten Bitmap-Daten wandern. Danach wird die Pumpe als eigener Thread gestartet und das Ziel-Bitmap wb erstellt. Durch die Zuweisung einer Source an MainImage wird sich das Steuerelement automatisch in wb bedienen. Man spart sich das manuelle Herumkopieren der Bilddaten. Interessant ist in diesem Zusammenhang noch das Formular. Es enthält nämlich neben dem vorhin gezeigten ViewFinder-Steuerelement auch ein Image, in das die von der ARGB-Frame-Pumpe erstellten WriteableBitmaps geblasen werden (Listing 7).



  
    
  




  
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -