Zdjęcia w Windows Phone 7  

Udostępnij na: Facebook

Autor: Marcin Kruszyński

Opublikowano: 2011-04-27

Pobierz i uruchom

Telefony z Windows Phone 7, podobnie jak inne nowoczesne telefony komórkowe, pozwalają robić zdjęcia i nimi zarządzać. Użytkownicy dość często korzystają z tych funkcjonalności – niewątpliwie warto zapoznać się z nimi od strony technicznej.

Po przeczytaniu tego artykułu będziesz:

  • umiał przekazać do swojej aplikacji zdjęcie zrobione aparatem lub wybrane z kolekcji na telefonie,
  • wiedział, jak odczytać kolekcję zdjęć na telefonie i jak zapisać do niej nowe zdjęcia,
  • znał API do kodowania i dekodowania zdjęć w formacie JPEG,
  • wiedział, jak można edytować zdjęcie na poziomie pikseli,
  • potrafił zintegrować swoją aplikację z systemową przeglądarką obrazków.

Wprowadzenie

Nasze aplikacje nie mają bezpośredniego dostępu do aparatu, który jest obsługiwany przez system operacyjny. Możemy jednak za pomocą choosera (rodzaj taska systemowego – więcej na ten temat możesz przeczytać w artykule Choosers) CameraCaptureTask włączyć aparat i zrobić nim zdjęcie, które zostanie zwrócone do naszej aplikacji.

Zdjęcia możemy przechowywać tak jak inne pliki w Isolated Storage (izolowana przestrzeń do trzymania plików przez aplikację, opisana w artykule Isolated Storage w Windows Phone 7), jednak  więcej zyskamy, gdy zapiszemy je do biblioteki Pictures (lokalna multimedialna baza danych Zune). Użytkownicy za pomocą systemowej aplikacji mogą przeglądać obrazki z biblioteki, wysyłać je w e-mailach lub wiadomościach tekstowych, udostępniać w różnych serwisach. Podczas synchronizacji telefonu z komputerem obrazki z biblioteki są przenoszone na komputer.

API w Windows Phone 7 daje nam dwa sposoby dostępu do obrazków z biblioteki Pictures. Możemy za pomocą PhotoChooserTask uruchomić przeglądarkę obrazków i wybrać dany obrazek, który zostanie zwrócony do naszej aplikacji. Inną możliwością jest skorzystanie z klasy MediaLibrary, która udostępnia nam wszystkie obrazki w postaci kolekcji oraz pozwala na zapisanie obrazka w bibliotece.

Interakcja przy obsłudze wybranego zdjęcia z biblioteki Pictures za pomocą naszej aplikacji może odbywać się również na dwa sposoby. Typowo uruchamiamy aplikację, a potem wybieramy w niej obrazek z biblioteki Pictures. Proces ten możemy odwrócić, a przez to uczynić wygodniejszym dla użytkownika. Wystarczy, że zintegrujemy naszą aplikację z systemową przeglądarką obrazków. Dostajemy się do niej, wybierając dowolny obrazek w hubie Pictures. Wtedy użytkownik najpierw wybiera zdjęcie spośród zdjęć zgromadzonych na telefonie, a potem otwiera naszą aplikację za pomocą menu Extras (funkcjonalność Photo Extras Application Extensibility) lub Share (funkcjonalność Share Picker Extensibility). Aby się do nich dostać, należy rozwinąć pasek aplikacyjny przeglądarki obrazków.

CameraCaptureTask i PhotoChooserTask

Przedstawimy praktyczny przykład, w którym za pomocą CameraCaptureTask robimy zdjęcie i wyświetlamy je w kontrolce Image. Niech task będzie wywoływany przez naciśnięcie przycisku. Całość realizuje poniższy kod:

CameraCaptureTask cameraCaptureTask;

public MainPage()
{
    InitializeComponent();
        
    cameraCaptureTask = new CameraCaptureTask();
    cameraCaptureTask.Completed += new
    EventHandler<PhotoResult>(cameraCaptureTask_Completed);            
}

void cameraCaptureTask_Completed(object sender, PhotoResult e)
{
    if (e.TaskResult == TaskResult.OK && e.ChosenPhoto != null)
    {
        WriteableBitmap bitmapImage = PictureDecoder.DecodeJpeg(e.ChosenPhoto);
        image.Source = bitmapImage; 
    }
}  

private void cameraCaptureButton_Click(object sender, RoutedEventArgs e)
{
    cameraCaptureTask.Show();
}

Przedstawiony kod jest podobny do dokładnie opisanego przykładu z PhotoChooserTask w artykule Choosers, gdzie zostały wymienione warunki poprawnej implementacji. Należy tutaj przypomnieć, że aplikacja wywołująca choosera może podlegać tombstoningowi.

Aparat jest przystosowany do używania w pozycji poziomej i tworzy zdjęcia w takim formacie. Nagłówki EXIF (Exchangeable Image Format) w zwracanym zdjęciu określają jego poprawną orientację. Wbudowana aplikacja obsługująca aparat automatycznie wyświetla obrazek w prawidłowej orientacji. W przypadku naszej aplikacji musimy zadbać o to sami. Szczegółowe informacje na ten temat znajdziesz w poście Tima Heuera – Handling picture orientation in CameraCaptureTask in Windows Phone 7.

Wykonywanie zdjęć możesz przetestować na emulatorze. Po otwarciu systemowej aplikacji zdjęcie robimy przez naciśnięcie przycisku w prawej górnej części ekranu. Symulowane zdjęcie wygląda jak biały prostokąt z małym czarnym prostokątem w jednym ze swoich rogów.

Edytowanie zdjęcia

W ostatnio prezentowanym przykładzie strumień ze zdjęciem JPEG jest dekodowany do obiektu klasy WriteableBitmap (umożliwia łatwe modyfikowanie bitmap na poziomie pojedynczych pikseli). Wykorzystujemy tutaj metodę DecodeJpeg z klasy PictureDecoder ze standardowego API w Windows Phone 7.

Niech nasza aplikacja zamienia kolorowe zdjęcia na czarno-białe:

private void monochromatizeButton_Click(object sender, RoutedEventArgs e)
{
    var wb = image.Source as WriteableBitmap;

    if (wb == null) return;

    byte a, r, b, g, max, min;
    int pixel;
    for (int i = 0; i < wb.Pixels.Length; i++)
    {
        pixel = wb.Pixels[i];
        a = (byte)(pixel >> 24);
        r = (byte)(pixel >> 16);
        g = (byte)(pixel >> 8);
        b = (byte)pixel;
                
        max = Math.Max(r, g);
        max = Math.Max(max, b);
                
        min = Math.Max(r, g);
        min = Math.Max(min, b);
                
        pixel = (max + min) / 2;
        wb.Pixels[i] = (a << 24) | (pixel << 16) | (pixel << 8) | pixel;
    }

    image.Source = wb;  
}

Efekty działania przedstawionego kodu obrazuje rysunek 1.

Rys.1. Edycja zdjęcia. Rys.2. Folder Saved Pictures.

                                                   

Zapisywanie zdjęcia w bibliotece Pictures

Zapiszmy poprzednio zmodyfikowany obraz w bibliotece Pictures:

private void saveButton_Click(object sender, EventArgs e)
{
    var wb = image.Source as WriteableBitmap;

    var memoryStream = new MemoryStream();
    wb.SaveJpeg(memoryStream, wb.PixelWidth, wb.PixelHeight, 0, 85);            
    memoryStream.Position = 0;           

    var library = new MediaLibrary();
    library.SavePicture(string.Format("{0}.jpg", Guid.NewGuid()), memoryStream);
}

Najpierw musimy zamienić obiekt WriteableBitmap na strumień zakodowany w formacie JPEG. Pomocna tutaj będzie extension metoda dla WriteableBitmap o nazwie SaveJpeg zdefiniowana w standardowej klasie Extensions. Oprócz strumienia podajemy docelowe rozmiary obrazka, jego orientację (parametr obecnie niewykorzystywany, zwykle podajemy wartość 0) i stopień kompresji (0-100 – ze względu na jakość nie jest zalecane podawanie wartości mniejszych niż 70). W prezentowanym przykładzie wynik kodowania zapisujemy do strumienia w pamięci. Aby mógł zostać ponownie użyty do zapisu przez MediaLibrary, trzeba wyzerować jego bieżącą pozycję.

Metoda SavePicture z klasy MediaLibrary przyjmuje strumień lub tablicę bajtów w formacie JPEG i umożliwia zapisanie pliku w tym formacie w bibliotece Pictures w folderze Saved Pictures (patrz rys. 2). Klasa MediaLibrary pochodzi z XNA. Aby z niej skorzystać w Silverlight, potrzebujemy dodać do projektu referencję do assembly Microsoft.Xna.Framework.Media.

W obecnie dostarczanej wersji emulatora nie mamy możliwości przeglądania obrazków w hubie Pictures, w tym katalogu Saved Pictures.

Wyświetlanie kolekcji obrazków z biblioteki Pictures

Za pomocą propercji Pictures klasy MediaLibrary zyskujemy dostęp do wszystkich obrazków w postaci kolekcji PictureCollection. Jej elementami są obiekty klasy Picture, która pozwala pobrać obrazek i jego miniaturę w postaci strumienia (odpowiednio metody GetImage i GetThumbnail) oraz posiada informacje o nazwie, albumie, dacie (propercje Name, Album, Data). Aby wyświetlić miniatury wszystkich obrazków, możemy posłużyć się następującym kodem (obiekty pomocniczej klasy MyPicture przechowują bitmapy, do których bindują kontrolki Image w UI):

var library = new MediaLibrary();
var pictures = library.Pictures.Select(p =>
            {
                var bitmapImage = new BitmapImage();
                bitmapImage.SetSource(p.GetThumbnail());

                return new MyPicture { Thumbnail = bitmapImage };
            });

Propercja SavedPictures klasy MediaLibrary zwraca kolekcję PictureCollection wszystkich obrazków z folderu Saved Pictures.

Photo Extras Application Extensibility

Rys.3. Korzystanie z menu Extras.

Photo Extras Application Extensibility sprawia, że użytkownik za pomocą menu Extras ma dostęp do naszej aplikacji bez opuszczania przeglądarki obrazków (patrz rys. 3). Funkcjonalność ta jest przeznaczona dla aplikacji, które głównie operują na zdjęciach i obrazkach (np. edytory graficzne). Menu Extras wyświetla się jedynie wtedy, gdy aplikacja wspierająca tę funkcjonalność jest zainstalowana na telefonie.

Aby nasza aplikacja została wykryta i umieszczona w menu Extras, potrzebujemy dodać do projektu plik o nazwie Extras.xml (z opcjami: Build Action ustawioną na wartość Content i Copy to Output Directory - Copy always). Jego zawartość wygląda następująco:

 

<?xml version="1.0" encoding="utf-8" ?>
<Extras>
  <PhotosExtrasApplication>
    <Enabled>true</Enabled>
  </PhotosExtrasApplication>
</Extras>

Przy uruchamianiu aplikacji za pomocą menu Extras zostaje przekazany do niej token. Służy on do pobrania obrazka z lokalnej multimedialnej bazy danych Zune (metoda GetPictureFromToken klasy MediaLibrary). Powinien być od razu wykorzystany.

protected override void OnNavigatedTo(NavigationEventArgs e)
{            
    var queryStrings = this.NavigationContext.QueryString;

    if (queryStrings.ContainsKey("token"))
    {
        MediaLibrary library = new MediaLibrary();
        Picture picture = library.GetPictureFromToken(queryStrings["token"]);

        BitmapImage bitmap = new BitmapImage();
        bitmap.SetSource(picture.GetImage());
        WriteableBitmap picLibraryImage = new WriteableBitmap(bitmap);
        image.Source = picLibraryImage;
    }
 }

Emulator dostarczany z obecną wersją narzędzi dla Windows Phone 7 nie daje możliwości przetestowania tej funkcjonalności, potrzebne jest fizyczne urządzenie.

Share Picker Extensibility

Rys.4. Korzystanie z menu Share.

Funkcjonalność Share Picker Extensibility w założeniu ma służyć do udostępnienia danego zdjęcia za pomocą wybranej usługi sieciowej. Po naciśnięciu pozycji Share... w menu przeglądarki obrazków zostaje wyświetlone okno z listą aplikacji (patrz rys. 4).  Oprócz domyślnych pozycji może tam zostać dodana nasza aplikacja.

Aby tak się stało, trzeba dodać do projektu plik XML o nazwie E0F0E49A-3EB1-4970-B780-45DA41EC7C28.xml (z takimi samymi opcjami ustawień jak plik Extras.xml).

Wybrane przez użytkownika zdjęcie pozyskujemy w aplikacji na podstawie przekazanego do niej parametru FileID. Kod realizujący tę czynność jest analogiczny do kodu zaprezentowanego przy funkcjonalności Photo Extras, jedynie nazwa parametru jest inna. O wysyłanie zdjęcia za pomocą web serwisu musimy zadbać sami. 

Aby przetestować działanie, podobnie jak w przypadku Photo Extras, potrzebujemy telefonu.

Podsumowanie

W tym artykule poznaliśmy API w Windows Phone 7 związane z obsługą zdjęć. Wiemy, jak zrobić zdjęcie za pomocą aparatu. Potrafimy odczytywać zdjęcia przechowywane w bibliotece Pictures na telefonie i zapisywać do niej nowe. Umiemy zamienić strumień JPEG do obiektu WriteableBitmap i odwrotnie. Zobaczyliśmy przykład prostej modyfikacji zdjęcia. Znamy możliwości integracji naszej aplikacji z wbudowaną przeglądarką obrazków.