Skip to content

Instantly share code, notes, and snippets.

@ynkdir
Last active September 21, 2025 17:13
Show Gist options
  • Save ynkdir/40168f3031f640295648c26638e28226 to your computer and use it in GitHub Desktop.
Save ynkdir/40168f3031f640295648c26638e28226 to your computer and use it in GitHub Desktop.
WinUI3 example for SoftwareBitmap
# How to show a PIL image in WinUI
import io
from PIL import Image
from win32more.Microsoft.UI.Xaml.Media.Imaging import BitmapImage, SoftwareBitmapSource
from win32more.Microsoft.Windows.Storage.Pickers import FileOpenPicker
from win32more.Windows.Graphics.Imaging import (
BitmapAlphaMode,
BitmapBufferAccessMode,
BitmapPixelFormat,
SoftwareBitmap,
)
from win32more.Windows.Storage.Streams import DataWriter, InMemoryRandomAccessStream
from win32more.Windows.Win32.System.WinRT import IMemoryBufferByteAccess
from win32more import FAILED, POINTER, Byte, UInt32, WinError
from win32more.appsdk.xaml import XamlApplication, XamlLoader
class App(XamlApplication):
def InitializeComponent(self):
# With WindowsAppSDK1.8.0, creating XamlControlsResources() fails.
# Disable default app.xaml loading.
pass
def OnLaunched(self, args):
self._window = XamlLoader.Load(
self,
"""<?xml version="1.0" encoding="utf-8"?>
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:controls="using:Microsoft.UI.Xaml.Controls">
<StackPanel>
<Button x:Name="Button1" Click="Button1_Click" Content="SoftwareBitmap" />
<Button x:Name="Button2" Click="Button2_Click" Content="BitmapImage" />
<Image x:Name="Image1" Width="500" Height="500" />
</StackPanel>
</Window>
""",
)
self._window.Activate()
async def Button1_Click(self, sender, e):
path = await self._pick_file()
if path is None:
return
image = Image.open(path)
self.Image1.Width = image.width
self.Image1.Height = image.height
self.Image1.Source = await self.create_software_bitmap_from_pil_image(image)
async def Button2_Click(self, sender, e):
path = await self._pick_file()
if path is None:
return
image = Image.open(path)
self.Image1.Width = image.width
self.Image1.Height = image.height
self.Image1.Source = await self.create_bitmap_from_pil_image(image)
async def _pick_file(self):
picker = FileOpenPicker(self._window.AppWindow.Id)
r = await picker.PickSingleFileAsync()
if not r:
return None
return r.Path
async def create_software_bitmap_from_pil_image(self, image):
bitmap = SoftwareBitmap(BitmapPixelFormat.Bgra8, image.width, image.height, BitmapAlphaMode.Premultiplied)
with bitmap.LockBuffer(BitmapBufferAccessMode.Write) as buffer:
with buffer.CreateReference() as reference:
byte_access = reference.as_(IMemoryBufferByteAccess)
p = POINTER(Byte)()
n = UInt32()
hr = byte_access.GetBuffer(p, n)
if FAILED(hr):
raise WinError(hr)
for i, (r, g, b, a) in enumerate(image.convert("RGBA").getdata()):
p[i * 4] = b
p[i * 4 + 1] = g
p[i * 4 + 2] = r
p[i * 4 + 3] = a
source = SoftwareBitmapSource()
await source.SetBitmapAsync(bitmap)
return source
async def create_bitmap_from_pil_image(self, image):
buf = io.BytesIO()
image.save(buf, format="PNG")
stream = InMemoryRandomAccessStream()
writer = DataWriter(stream)
writer.WriteBytes(buf.getvalue())
await writer.StoreAsync()
await writer.FlushAsync()
stream.Seek(0)
bitmap = BitmapImage()
bitmap.SetSource(stream)
return bitmap
XamlApplication.Start(App)
# WinUI3 example for SoftwareBitmap.
from win32more import FAILED, POINTER, Byte, UInt32, WinError
from win32more.appsdk.xaml import XamlApplication, XamlLoader
from win32more.Microsoft.UI.Xaml.Media.Imaging import SoftwareBitmapSource
from win32more.Windows.Graphics.Imaging import (
BitmapAlphaMode,
BitmapBufferAccessMode,
BitmapPixelFormat,
SoftwareBitmap,
)
from win32more.Windows.Win32.System.WinRT import IMemoryBufferByteAccess
class App(XamlApplication):
def OnLaunched(self, args):
self._window = XamlLoader.Load(
self,
"""<?xml version="1.0" encoding="utf-8"?>
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:controls="using:Microsoft.UI.Xaml.Controls">
<StackPanel>
<Button x:Name="Button1" Click="Button1_Click" Content="Red" />
<Button x:Name="Button2" Click="Button2_Click" Content="Blue" />
<Image x:Name="Image1" Width="100" Height="100" />
</StackPanel>
</Window>
""",
)
self._window.Activate()
async def Button1_Click(self, sender, e):
bitmap = self.create_software_bitmap(100, 100, 255, 0, 0)
source = SoftwareBitmapSource()
await source.SetBitmapAsync(bitmap)
self.Image1.Source = source
async def Button2_Click(self, sender, e):
bitmap = self.create_software_bitmap(100, 100, 0, 0, 255)
source = SoftwareBitmapSource()
await source.SetBitmapAsync(bitmap)
self.Image1.Source = source
def create_software_bitmap(self, width, height, r, g, b):
bitmap = SoftwareBitmap(BitmapPixelFormat.Bgra8, width, height, BitmapAlphaMode.Premultiplied)
with bitmap.LockBuffer(BitmapBufferAccessMode.Write) as buffer:
with buffer.CreateReference() as reference:
byte_access = reference.as_(IMemoryBufferByteAccess)
p = POINTER(Byte)()
n = UInt32()
hr = byte_access.GetBuffer(p, n)
if FAILED(hr):
raise WinError(hr)
for i in range(0, n.value, 4):
p[i] = b
p[i + 1] = g
p[i + 2] = r
p[i + 3] = 255
return bitmap
XamlApplication.Start(App)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment