Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym artykule wyjaśniono, jak ładować i zapisywać pliki obrazów przy użyciu BitmapDecoder i BitmapEncoder i jak używać obiektu SoftwareBitmap do reprezentowania obrazów map bitowych.
Klasa SoftwareBitmap to wszechstronny interfejs API, który można utworzyć na podstawie wielu źródeł, w tym plików obrazów, obiektów WriteableBitmap , powierzchni Direct3D i kodu. ProgramBitmap umożliwia łatwą konwersję między różnymi formatami pikseli i trybami alfa oraz umożliwia dostęp niskiego poziomu do danych pikseli. Ponadto SoftwareBitmap jest typowym interfejsem używanym przez wiele funkcji Windows, w tym:
CapturedFrame umożliwia uzyskiwanie ramek zarejestrowanych przez aparat w postaci obiektu SoftwareBitmap.
VideoFrame umożliwia uzyskanie SoftwareBitmap reprezentacji VideoFrame.
FaceDetector umożliwia wykrywanie twarzy w SoftwareBitmap.
Przykładowy kod w tym artykule używa interfejsów API z następujących przestrzeni nazw.
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;
using Windows.Graphics.Imaging;
using Microsoft.UI.Xaml.Media.Imaging;
using System.Runtime.InteropServices.WindowsRuntime;
Tworzenie mapy SoftwareBit na podstawie pliku obrazu za pomocą narzędzia BitmapDecoder
Aby utworzyć element SoftwareBitmap z pliku, uzyskaj wystąpienie StorageFile zawierające dane obrazu. W tym przykładzie użyto FileOpenPicker aby umożliwić użytkownikowi wybranie pliku obrazu.
private async Task<StorageFile?> PickInputFileAsync()
{
var picker = new FileOpenPicker();
// Initialize the picker with the window handle (required for WinUI 3).
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd);
picker.ViewMode = PickerViewMode.Thumbnail;
picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
picker.FileTypeFilter.Add(".jpg");
picker.FileTypeFilter.Add(".jpeg");
picker.FileTypeFilter.Add(".png");
picker.FileTypeFilter.Add(".bmp");
return await picker.PickSingleFileAsync();
}
Wywołaj metodę OpenAsync obiektu StorageFile , aby uzyskać strumień dostępu losowego zawierającego dane obrazu. Wywołaj metodę statyczną BitmapDecoder.CreateAsync aby uzyskać wystąpienie BitmapDecoder klasy dla określonego strumienia. Wywołaj GetSoftwareBitmapAsync aby uzyskać obiekt SoftwareBitmap zawierający obraz.
private async Task<SoftwareBitmap> CreateSoftwareBitmapFromFileAsync(StorageFile file)
{
using IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read);
// Create a decoder from the image file.
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
// Get the SoftwareBitmap representation of the file.
SoftwareBitmap softwareBitmap = await decoder.GetSoftwareBitmapAsync();
return softwareBitmap;
}
Zapisywanie mapy SoftwareBitmap w pliku za pomocą narzędzia BitmapEncoder
Aby zapisać obiekt SoftwareBitmap do pliku, uzyskaj wystąpienie klasy StorageFile, do którego zostanie zapisany obraz. W tym przykładzie użyto FileSavePicker aby umożliwić użytkownikowi wybranie pliku wyjściowego.
private async Task<StorageFile?> PickOutputFileAsync()
{
var picker = new FileSavePicker();
// Initialize the picker with the window handle (required for WinUI 3).
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd);
picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
picker.SuggestedFileName = "output";
picker.FileTypeChoices.Add("JPEG Image", new List<string> { ".jpg" });
picker.FileTypeChoices.Add("PNG Image", new List<string> { ".png" });
return await picker.PickSaveFileAsync();
}
Wywołaj metodę OpenAsync obiektu StorageFile , aby uzyskać losowy strumień dostępu, do którego zostanie zapisany obraz. Wywołaj metodę statyczną BitmapEncoder.CreateAsync, aby uzyskać wystąpienie BitmapEncoder klasy dla określonego strumienia. Pierwszym parametrem metody CreateAsync jest identyfikator GUID reprezentujący koder kodujący, który powinien służyć do kodowania obrazu. BitmapEncoder , klasa uwidacznia właściwość zawierającą identyfikator dla każdego kodera obsługiwanego przez koder, na przykład JpegEncoderId.
Użyj metody SetSoftwareBitmap , aby ustawić obraz, który zostanie zakodowany. Można ustawić wartości właściwości BitmapTransform, aby zastosować podstawowe przekształcenia do obrazu podczas jego kodowania. Właściwość IsThumbnailGenerated określa, czy miniatura jest generowana przez koder. Pamiętaj, że nie wszystkie formaty plików obsługują miniatury, więc jeśli używasz tej funkcji, powinieneś przechwycić błąd nieobsługiwanej operacji, który zostanie zgłoszony, jeśli miniatury nie są obsługiwane.
Wywołaj metodę FlushAsync , aby spowodować, że koder zapisuje dane obrazu w określonym pliku.
private async Task SaveSoftwareBitmapToFileAsync(SoftwareBitmap softwareBitmap, StorageFile outputFile)
{
using IRandomAccessStream stream = await outputFile.OpenAsync(FileAccessMode.ReadWrite);
// Create an encoder with the desired format.
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
// Set the software bitmap.
encoder.SetSoftwareBitmap(softwareBitmap);
// Set additional encoding parameters (optional).
encoder.BitmapTransform.ScaledWidth = (uint)softwareBitmap.PixelWidth;
encoder.BitmapTransform.ScaledHeight = (uint)softwareBitmap.PixelHeight;
encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;
encoder.IsThumbnailGenerated = true;
try
{
await encoder.FlushAsync();
}
catch (Exception ex)
{
const int WINCODEC_ERR_UNSUPPORTEDOPERATION = unchecked((int)0x88982F81);
switch (ex.HResult)
{
case WINCODEC_ERR_UNSUPPORTEDOPERATION:
// If the encoder does not support thumbnail generation,
// disable it and try again.
encoder.IsThumbnailGenerated = false;
break;
default:
throw;
}
}
if (!encoder.IsThumbnailGenerated)
{
await encoder.FlushAsync();
}
}
Podczas tworzenia obiektu BitmapEncoder można określić dodatkowe opcje kodowania, tworząc nowy obiekt BitmapPropertySet i wypełniając go co najmniej jednym obiektem BitmapTypedValue reprezentującym ustawienia kodera. Aby uzyskać listę obsługiwanych opcji kodera, zobacz BitmapEncoder options reference (Dokumentacja opcji bitmapencodera).
private async Task SaveWithEncodingOptionsAsync(SoftwareBitmap softwareBitmap, StorageFile outputFile)
{
using IRandomAccessStream stream = await outputFile.OpenAsync(FileAccessMode.ReadWrite);
// Create encoding options with a specific image quality.
var propertySet = new BitmapPropertySet();
var qualityValue = new BitmapTypedValue(0.9, Windows.Foundation.PropertyType.Single);
propertySet.Add("ImageQuality", qualityValue);
// Create the encoder with the encoding options.
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(
BitmapEncoder.JpegEncoderId, stream, propertySet);
encoder.SetSoftwareBitmap(softwareBitmap);
await encoder.FlushAsync();
}
Używanie mapy SoftwareBit z kontrolką obrazu XAML
Aby wyświetlić obraz na stronie XAML przy użyciu kontrolki Obraz , najpierw zdefiniuj kontrolkę Obraz na stronie XAML.
<Image x:Name="imageControl"
Grid.Row="2"
Stretch="Uniform"/>
Obecnie kontrolka Image obsługuje tylko obrazy w kodowaniu BGRA8 oraz z wstępnie przemnożonym kanałem alfa lub bez kanału alfa. Przed podjęciem próby wyświetlenia obrazu przetestuj, aby upewnić się, że ma on poprawny format, a jeśli nie, użyj metody static ConvertSoftwareBitmap, aby przekonwertować obraz na obsługiwany format.
Utwórz nowy obiekt SoftwareBitmapSource . Ustaw zawartość obiektu źródłowego, wywołując metodę SetBitmapAsync, przekazując element SoftwareBitmap. Następnie możesz ustawić właściwość Source kontrolki Obraz na nowo utworzoną właściwość SoftwareBitmapSource.
private async Task DisplaySoftwareBitmapAsync(SoftwareBitmap softwareBitmap)
{
// SoftwareBitmap must be Bgra8 with premultiplied or no alpha
// to display in a XAML Image control.
if (softwareBitmap.BitmapPixelFormat != BitmapPixelFormat.Bgra8 ||
softwareBitmap.BitmapAlphaMode == BitmapAlphaMode.Straight)
{
softwareBitmap = SoftwareBitmap.Convert(
softwareBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
}
// In WinUI 3, SoftwareBitmapSource is in Microsoft.UI.Xaml.Media.Imaging.
var source = new SoftwareBitmapSource();
await source.SetBitmapAsync(softwareBitmap);
// Set the source of the Image control.
imageControl.Source = source;
}
Możesz również użyć SoftwareBitmapSource, aby ustawić obiekt SoftwareBitmap jako element ImageSource dla obiektu ImageBrush.
Utwórz obiekt SoftwareBitmap z obiektu WriteableBitmap
Mapę SoftwareBitmap można utworzyć na podstawie istniejącej mapy WriteableBitmap , wywołując funkcję SoftwareBitmap.CreateCopyFromBuffer i podając właściwość PixelBuffermapy WriteableBitmap , aby ustawić dane pikseli. Drugi argument umożliwia określenie formatu pikseli dla nowo utworzonej mapy SoftwareBitmap. Możesz użyć właściwości PixelWidth i PixelHeight obiektu WriteableBitmap, aby określić wymiary nowego obrazu.
private SoftwareBitmap ConvertWriteableBitmapToSoftwareBitmap(WriteableBitmap writeableBitmap)
{
// Create a SoftwareBitmap from the WriteableBitmap's pixel buffer.
SoftwareBitmap softwareBitmap = SoftwareBitmap.CreateCopyFromBuffer(
writeableBitmap.PixelBuffer,
BitmapPixelFormat.Bgra8,
writeableBitmap.PixelWidth,
writeableBitmap.PixelHeight);
return softwareBitmap;
}
Programowe tworzenie lub edytowanie mapy SoftwareBitmap
Do tej pory ten temat dotyczył pracy z plikami obrazów. Możesz również programowo utworzyć nową mapę SoftwareBitmap w kodzie i użyć tej samej techniki, aby uzyskać dostęp do danych pikseli i zmodyfikować je.
Użyj metody CopyFromBuffer , aby wypełnić mapę SoftwareBitmap z tablicy bajtów i copyToBuffer , aby skopiować dane pikseli do tablicy bajtów do odczytu lub modyfikacji. Aby użyć metody rozszerzenia AsBuffer w celu opakowania tablicy bajtów jako IBuffer, uwzględnij System.Runtime.InteropServices.WindowsRuntime przestrzeni nazw (znajduje się to w SnippetNamespaces przy użyciu instrukcji w górnej części tego artykułu).
Utwórz nową mapę SoftwareBitmap z żądanym formatem pikseli i rozmiarem. Przydziel tablicę bajtów wystarczająco dużą, aby przechowywać dane pikseli, wypełnić je żądanymi wartościami, a następnie wywołać metodę CopyFromBuffer , aby zapisać dane w mapie bitowej.
private SoftwareBitmap CreateGradientBitmap(int width, int height)
{
// Create a new SoftwareBitmap programmatically.
var softwareBitmap = new SoftwareBitmap(
BitmapPixelFormat.Bgra8, width, height, BitmapAlphaMode.Premultiplied);
// Allocate a byte array for the pixel data.
int bytesPerPixel = 4; // BGRA8
byte[] pixelData = new byte[width * height * bytesPerPixel];
// Fill the bitmap with a gradient pattern.
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
int pixelIndex = (row * width + col) * bytesPerPixel;
// Blue channel: gradient left to right.
pixelData[pixelIndex + 0] = (byte)((double)col / width * 255);
// Green channel: gradient top to bottom.
pixelData[pixelIndex + 1] = (byte)((double)row / height * 255);
// Red channel: inverse diagonal gradient.
pixelData[pixelIndex + 2] = (byte)(255 - (((double)(col + row)
/ (width + height)) * 255));
// Alpha channel: fully opaque.
pixelData[pixelIndex + 3] = 255;
}
}
// Copy the pixel data into the SoftwareBitmap.
softwareBitmap.CopyFromBuffer(pixelData.AsBuffer());
return softwareBitmap;
}
Tworzenie mapy SoftwareBit na podstawie powierzchni Direct3D
Aby utworzyć obiekt SoftwareBitmap z powierzchni Direct3D, należy uwzględnić w projekcie przestrzeń nazw Windows.Graphics.DirectX.Direct3D11.
using Windows.Graphics.DirectX.Direct3D11;
Wywołaj metodę CreateCopyFromSurfaceAsync , aby utworzyć nową mapę SoftwareBitmap na powierzchni. Jak wskazuje nazwa, nowa mapa SoftwareBitmap ma oddzielną kopię danych obrazu. Modyfikacje mapy SoftwareBit nie będą miały żadnego wpływu na powierzchnię Direct3D.
private async Task<SoftwareBitmap> CreateBitmapFromSurfaceAsync(IDirect3DSurface surface)
{
// Create a SoftwareBitmap from a Direct3D surface.
SoftwareBitmap softwareBitmap =
await SoftwareBitmap.CreateCopyFromSurfaceAsync(surface);
return softwareBitmap;
}
Konwertowanie mapy SoftwareBit na inny format pikseli
Klasa SoftwareBitmap udostępnia metodę statyczną Convert, która umożliwia łatwe tworzenie nowej mapy SoftwareBitmap używającej formatu pikseli i trybu alfa określonego na podstawie istniejącej mapy SoftwareBit. Pamiętaj, że nowo utworzona mapa bitowa ma oddzielną kopię danych obrazu. Modyfikacje nowej mapy bitowej nie będą mieć wpływu na mapę bitową źródłową.
private SoftwareBitmap ConvertBitmapPixelFormat(SoftwareBitmap softwareBitmap)
{
// Convert the pixel format and alpha mode of the SoftwareBitmap.
SoftwareBitmap convertedBitmap = SoftwareBitmap.Convert(
softwareBitmap,
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Premultiplied);
return convertedBitmap;
}
Transkodowanie pliku obrazu
Plik obrazu można transkodować bezpośrednio z BitmapDecoder do BitmapEncoder. Utwórz IRandomAccessStream z pliku do transkodowania. Utwórz nowy BitmapDecoder ze strumienia wejściowego. Utwórz nowy InMemoryRandomAccessStream, aby koder mógł do niego zapisywać, i wywołaj BitmapEncoder.CreateForTranscodingAsync, przekazując strumień w pamięci i obiekt dekodera. Opcje kodowania nie są obsługiwane podczas transkodowania; zamiast tego należy użyć polecenia CreateAsync. Wszystkie właściwości w pliku obrazu wejściowego, które nie zostały specjalnie ustawione w koderze, zostaną zapisane w pliku wyjściowym bez zmian. Wywołaj metodę FlushAsync, aby spowodować, że koder zakoduje dane do strumienia w pamięci. Na koniec poszukaj strumienia plików i strumienia w pamięci na początku, a następnie wywołaj metodę CopyAsync , aby zapisać strumień w pamięci do strumienia plików.
private async Task TranscodeImageFileAsync(StorageFile inputFile, StorageFile outputFile)
{
using IRandomAccessStream inputStream =
await inputFile.OpenAsync(FileAccessMode.Read);
using IRandomAccessStream outputStream =
await outputFile.OpenAsync(FileAccessMode.ReadWrite);
// Create a decoder for the input file.
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(inputStream);
// Create an encoder for transcoding to the output file.
BitmapEncoder encoder =
await BitmapEncoder.CreateForTranscodingAsync(outputStream, decoder);
// Optionally apply transforms during transcoding.
encoder.BitmapTransform.ScaledWidth = decoder.PixelWidth / 2;
encoder.BitmapTransform.ScaledHeight = decoder.PixelHeight / 2;
encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;
await encoder.FlushAsync();
}