Jakub Florczyk - Blog o programowaniu .NET i Android

Programista praktyczny

Month: August, 2009

HTC Touch Pro2 – skórka emulatora

Pisząc programy pod .NET Compact Framework Microsoft dostarcza z Windows Mobile SDK zestaw emulatora urządzenia i skórek pod różne rozdzielczości. Znudzony trochę nieciekawym wyglądem postanowiłem stworzyć własną skórkę. A skoro firma HTC udostępniła HTC Touch Pro2 do testów, wybór był oczywisty.

Tworzenie skórki jest dziecinnie proste (dlatego dziwi mnie, że tak mało jest ich w sieci). Cała zabawa polega na stworzeniu grafiki urządzenia i pliku definicji. W skórkach Microsoft dodatkowo występuje grafika maski klawiszy oraz grafika “wciśniętych” przycisków, ale przez sam emulator nie są one wymagane.
Plik definicji wskazuje na poszczególne grafiki, położenie ekranu oraz listę obsługiwanych klawiszy jeżeli dostarczyliśmy grafikę maski. Więcej o tworzeniu skórek można poczytać na MSDN-ie.

Skórka emulatora HTC Touch Pro2:

HTC_Touch_Pro2_Emulator

Skórkę można pobrać tutaj.

Ponieważ HTC Touch Pro2 ma ekran o rozdzielczości WCGA (480×800) do tej skórki wymagany jest emulator “Windows Mobile 6.5 Professional WVGA Emulator”. który jest dostarczany w Windows Mobile 6.5 Developer Tool Kit.

Sposób instalacji:

  • pliki wypakowujemy do C:\Program Files\Windows Mobile 6 SDK\PocketPC\DeviceemulationV650\
  • uruchamiamy Visual Studio
  • nawigujemy do Tools -> Options…
  • wybieramy Device Tools -> Devices
  • z listy wybieramy USA Windows Mobile 6.5 Professional WVGA Emulator
  • klikamy Save As…
  • wprowadzamy tytuł HTC Touch Pro2
  • klikamy Properties…
  • klikamy Emulator Options…
  • przełączamy się na zakładkę Display
  • w Skin wskazujemy ścieżkę do C:\Program Files\Windows Mobile 6 SDK\PocketPC\DeviceemulationV650\HTC_Touch_Pro2\HTC_Touch_Pro2.xml
  • uruchamiamy nasz program

Ze względów praktycznych klawisz Back zmieniłem na App Launch 2 aby możliwe było obracanie emulatora. Ponieważ HTC Touch Pro2 ma duży ekran, emulator ledwo mieści się nawet na monitorze o rozdzielczości 1920×1200 :)

Surface Blip – prezentacja

Nagranie prezentujące użycie Surface Blip na emulatorze. Miłego oglądania :)

Przezroczyste grafiki w .NET Compact Framework

Popełniłem wpis o półprzezroczystych grafikach w .NET Compact Framework. Czas na opis realizacji rysowania grafik z pełnym wsparciem kanału alfa dla każdego pixla z osobna.

Cała operacja wbrew pozorom jest stosunkowo prosta (mimo że obszerna) i opiera się na wrapowaniu kodu z klas gdipluspixelformats.h oraz imaging.h.

Tak wygląda przykładowy GraphicsExtension, który będzie za nas realizował operację rysowania:

    // Pulled from gdipluspixelformats.h in the Windows Mobile 5.0 Pocket PC SDK
    public enum PixelFormatID : int
    {
        PixelFormatIndexed = 0x00010000,  // Indexes into a palette
        PixelFormatGDI = 0x00020000,      // Is a GDI-supported format
        PixelFormatAlpha = 0x00040000,    // Has an alpha component
        PixelFormatPAlpha = 0x00080000,   // Pre-multiplied alpha
        PixelFormatExtended = 0x00100000, // Extended color 16 bits/channel
        PixelFormatCanonical = 0x00200000,
        PixelFormatUndefined = 0,
        PixelFormatDontCare = 0,
        PixelFormat1bppIndexed = (1 | (1<<8) | PixelFormatIndexed | PixelFormatGDI),
        PixelFormat4bppIndexed = (2 | (4<<8) | PixelFormatIndexed | PixelFormatGDI),
        PixelFormat8bppIndexed = (3 | (8<<8) | PixelFormatIndexed | PixelFormatGDI),
        PixelFormat16bppRGB555 = (5 | (16<<8) | PixelFormatGDI),
        PixelFormat16bppRGB565 = (6 | (16<<8) | PixelFormatGDI),
        PixelFormat16bppARGB1555 = (7 | (16<<8) | PixelFormatAlpha | PixelFormatGDI),
        PixelFormat24bppRGB = (8 | (24<<8) | PixelFormatGDI),
        PixelFormat32bppRGB = (9 | (32<<8) | PixelFormatGDI),
        PixelFormat32bppARGB = (10 | (32<<8) | PixelFormatAlpha | PixelFormatGDI | PixelFormatCanonical),
        PixelFormat32bppPARGB = (11 | (32<<8) | PixelFormatAlpha | PixelFormatPAlpha | PixelFormatGDI),
        PixelFormat48bppRGB = (12 | (48<<8) | PixelFormatExtended),
        PixelFormat64bppARGB = (13 | (64<<8) | PixelFormatAlpha | PixelFormatCanonical | PixelFormatExtended),
        PixelFormat64bppPARGB = (14 | (64<<8) | PixelFormatAlpha | PixelFormatPAlpha | PixelFormatExtended),
        PixelFormatMax = 15
    }

    // Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
    public enum BufferDisposalFlag : int
    {
        BufferDisposalFlagNone,
        BufferDisposalFlagGlobalFree,
        BufferDisposalFlagCoTaskMemFree,
        BufferDisposalFlagUnmapView
    }

    // Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
    public enum InterpolationHint : int
    {
        InterpolationHintDefault,
        InterpolationHintNearestNeighbor,
        InterpolationHintBilinear,
        InterpolationHintAveraging,
        InterpolationHintBicubic
    }

    // Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
    public struct ImageInfo
    {
        // I am being lazy here, I don't care at this point about the RawDataFormat GUID
        public uint GuidPart1;
        public uint GuidPart2;
        public uint GuidPart3;
        public uint GuidPart4;
        public PixelFormatID pixelFormat;
        public uint Width;
        public uint Height;
        public uint TileWidth;
        public uint TileHeight;
        public double Xdpi;
        public double Ydpi;
        public uint Flags;
    }

    // Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
    [ComImport, Guid("327ABDA7-072B-11D3-9D7B-0000F81EF32E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [ComVisible(true)]
    public interface IImagingFactory
    {
        uint CreateImageFromStream(); // This is a place holder
        uint CreateImageFromFile(string filename, out IImage image);
        // We need the MarshalAs attribute here to keep COM interop from sending the buffer down as a Safe Array.
        uint CreateImageFromBuffer([MarshalAs(UnmanagedType.LPArray)] byte[] buffer, uint size, BufferDisposalFlag disposalFlag, out IImage image);
        uint CreateNewBitmap();            // This is a place holder
        uint CreateBitmapFromImage();      // This is a place holder
        uint CreateBitmapFromBuffer();     // This is a place holder
        uint CreateImageDecoder();         // This is a place holder
        uint CreateImageEncoderToStream(); // This is a place holder
        uint CreateImageEncoderToFile();   // This is a place holder
        uint GetInstalledDecoders();       // This is a place holder
        uint GetInstalledEncoders();       // This is a place holder
        uint InstallImageCodec();          // This is a place holder
        uint UninstallImageCodec();        // This is a place holder
    }

    // Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
    [ComImport, Guid("327ABDA9-072B-11D3-9D7B-0000F81EF32E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [ComVisible(true)]
    public interface IImage
    {
        uint GetPhysicalDimension(out Size size);
        uint GetImageInfo(out ImageInfo info);
        uint SetImageFlags(uint flags);
        // "Correct" declaration: uint Draw(IntPtr hdc, ref Rectangle dstRect, ref Rectangle srcRect);
        uint Draw(IntPtr hdc, ref Rectangle dstRect, IntPtr NULL);
        uint PushIntoSink(); // This is a place holder
        uint GetThumbnail(uint thumbWidth, uint thumbHeight, out IImage thumbImage);
    }

    public static class GraphicsExtension
    {
        public static void DrawImageAlphaChannel(this Graphics gx, IImage image, int x, int y)
        {
            ImageInfo imageInfo = new ImageInfo();
            image.GetImageInfo(out imageInfo);
            Rectangle rc = new Rectangle(x, y, (int)imageInfo.Width + x, (int)imageInfo.Height + y);
            IntPtr hdc = gx.GetHdc();
            image.Draw(hdc, ref rc, IntPtr.Zero);
            gx.ReleaseHdc(hdc);
        }
    }

Moja testowa grafika wygląda tak:
AplhaTest

Przykładowy kod testujący rysowanie:

            // load image (np w kostruktorze)
            IImagingFactory factory = (IImagingFactory)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("327ABDA8-072B-11D3-9D7B-0000F81EF32E")));
            factory.CreateImageFromFile(@"Program Files\SmartDeviceProject1\AplhaTest.png", out imagingResource);

            this.BackColor = Color.Red;

            // draw (np w OnPaint albo pochodnej metodzie)
            e.Graphics.DrawImageAlphaChannel(imagingResource, 10, 10);

Wynik wygląda następująco:
AlphaScreen

Jak widać wszystko wygląda poprawnie. Gradient poprawnie przykrywa czerwone tło kontrolki. Napisy także wyglądają dobrze.

Tyle jeśli chodzi o kod. Należy jednak zwrócić uwagę na dwa aspekty. Myślę, że ci bardziej spostrzegawczy zauważyli że nie użyłem żadnego standardowego sposobu ładowania grafik z CF (FileOpen lub zasoby). Do ładowania grafik do powyższej metody można tylko i wyłącznie wykorzystywać IImagingFactory. Oczywiście sugeruje zrobić singletona, bo wystarczy nam jedna instancja na cały projekt.
Drugi ważny aspekt to prędkość. I tutaj jest dramatycznie, całość operacji jest zrealizowana via odwołania Com-owskie, do tego dochodzi brak wsparcia na naszych urządzeniach co daje w wyniku dramatyczną prędkość. Jednak nie należy się zrażać i można spokojnie używać tych metod dla uzyskania naprawdę ładnych interfejsów.

FlakerNET 2.0

Zaktualizowałem FlakerNET do ostatnich zmian API wprowadzonych przez twórców Flakera.

Nowe funkcjonalności to m.in.:
- pobieranie obserwowanych
- pobieranie obserwujących
- pobieranie wiadomości obserwowanych tagów
- pobieranie wiadomości przyjaciół
- wyszukiwanie
- itp.