Skip to content

Commit 30f4dce

Browse files
authored
Merge pull request #65 from AvaloniaUI/fixes/gtk-offscreen-buffer-pixel-format
GTK: Offscreen pixel buffer fixes and several marshalling issues
2 parents 60460d0 + 4e3f2df commit 30f4dce

16 files changed

Lines changed: 319 additions & 99 deletions

src/Avalonia.Controls.WebView.Core/Gtk/GdkEvent.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ internal struct GdkEventScroll
9494
public Double x_root, y_root;
9595
public Double delta_x;
9696
public Double delta_y;
97-
public bool is_stop;//public guint is_stop : 1;
97+
public UInt32 is_stop;
9898
}
9999

100100
[StructLayout(LayoutKind.Sequential)]
@@ -110,7 +110,7 @@ internal struct GdkEventCrossing
110110
public Double x_root, y_root;
111111
public Int32 mode;
112112
public Int32 detail;
113-
public bool focus;
113+
public Int32 focus;
114114
public UInt32 state;
115115
}
116116

@@ -154,5 +154,5 @@ internal unsafe struct GdkEventKey
154154
public Byte *_string;
155155
public UInt16 hardware_keycode;
156156
public Byte group;
157-
public bool is_modifier;//public guint is_modifier : 1;
157+
public UInt32 is_modifier;
158158
}

src/Avalonia.Controls.WebView.Core/Gtk/GtkInterop.cs

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ internal static partial IntPtr webkit_website_data_manager_new(
8585
[DllImport(LibWebKit)]
8686
internal static extern IntPtr webkit_web_view_get_uri(IntPtr webView);
8787

88-
[DllImport(LibGio)]
88+
[DllImport(LibGLib)]
8989
internal static extern void g_free(IntPtr ptr);
9090

9191
[DllImport(LibGio)]
@@ -95,7 +95,7 @@ internal static partial IntPtr webkit_website_data_manager_new(
9595
internal static extern IntPtr g_main_context_default();
9696

9797
[LibraryImport(LibGLib)]
98-
[return: MarshalAs(UnmanagedType.I1)]
98+
[return: MarshalAs(UnmanagedType.Bool)]
9999
internal static partial bool g_main_context_is_owner(IntPtr context);
100100

101101
[DllImport(LibWebKit)]
@@ -105,7 +105,7 @@ internal static partial IntPtr webkit_website_data_manager_new(
105105
internal static extern void webkit_user_content_manager_add_script(IntPtr manager, IntPtr userScript);
106106

107107
[LibraryImport(LibWebKit, StringMarshalling = StringMarshalling.Utf8)]
108-
[return: MarshalAs(UnmanagedType.I1)]
108+
[return: MarshalAs(UnmanagedType.Bool)]
109109
internal static partial bool webkit_user_content_manager_register_script_message_handler(IntPtr manager, string messageHandler);
110110

111111
[LibraryImport(LibWebKit, StringMarshalling = StringMarshalling.Utf8)]
@@ -174,10 +174,10 @@ internal static partial IntPtr webkit_website_data_manager_new(
174174
[DllImport(LibWebKit)]
175175
public static extern void webkit_print_operation_set_page_setup(IntPtr operation, IntPtr pageSetup);
176176

177-
[DllImport(LibWebKit)]
177+
[DllImport(LibGtk)]
178178
public static extern IntPtr gtk_print_settings_new();
179179

180-
[LibraryImport(LibWebKit, StringMarshalling = StringMarshalling.Utf8)]
180+
[LibraryImport(LibGtk, StringMarshalling = StringMarshalling.Utf8)]
181181
internal static partial void gtk_print_settings_set(IntPtr settings, string key, string value);
182182

183183
[DllImport(LibGtk)]
@@ -218,8 +218,8 @@ internal static partial IntPtr webkit_website_data_manager_new(
218218
[DllImport(LibGtk)]
219219
internal static extern void gtk_widget_destroy(IntPtr widget);
220220

221-
[DllImport(LibGObject)]
222-
internal static extern ulong g_error_free(GError* error);
221+
[DllImport(LibGLib)]
222+
internal static extern void g_error_free(GError* error);
223223

224224
[DllImport(LibGObject)]
225225
internal static extern IntPtr g_object_ref(IntPtr handle);
@@ -230,8 +230,8 @@ internal static partial IntPtr webkit_website_data_manager_new(
230230
[DllImport (LibGObject)]
231231
internal static extern void g_object_unref(IntPtr handle);
232232

233-
[LibraryImport(LibGObject, StringMarshalling = StringMarshalling.Utf8)]
234-
internal static partial void g_variant_get(IntPtr variant, string formatString, out string? result);
233+
[LibraryImport(LibGLib, StringMarshalling = StringMarshalling.Utf8)]
234+
internal static partial void g_variant_get(IntPtr variant, string formatString, out IntPtr result);
235235

236236
[DllImport(LibGtk)]
237237
internal static extern IntPtr gtk_window_new(int type);
@@ -312,7 +312,7 @@ internal static partial IntPtr webkit_website_data_manager_new(
312312
internal static extern IntPtr gdk_keymap_get_for_display(IntPtr display);
313313

314314
[LibraryImport (LibGdk)]
315-
[return: MarshalAs(UnmanagedType.I1)]
315+
[return: MarshalAs(UnmanagedType.Bool)]
316316
internal static partial bool gdk_keymap_translate_keyboard_state(IntPtr keymap, uint hardware_keycode, GdkModifierType state, int group, out uint keyval, out int effective_group, out int level, out int consumed_modifiers);
317317

318318
[DllImport(LibGdk)]
@@ -351,10 +351,10 @@ internal static partial IntPtr webkit_website_data_manager_new(
351351
[DllImport(LibGdk)]
352352
public static extern IntPtr gdk_event_new(GdkEventType type);
353353

354-
[DllImport(LibGdk)]
354+
[DllImport(LibGtk)]
355355
public static extern bool gtk_widget_event(IntPtr widget, IntPtr gdkEvent);
356356

357-
[DllImport(LibGdk)]
357+
[DllImport(LibGtk)]
358358
public static extern void gtk_main_do_event(IntPtr gdkEvent);
359359

360360
[DllImport(LibGdk)]
@@ -453,12 +453,13 @@ internal static partial IntPtr webkit_website_data_manager_new(
453453
public static extern void soup_message_headers_foreach(
454454
IntPtr headers,
455455
delegate* unmanaged[Cdecl]<IntPtr, IntPtr, IntPtr, void> func,
456-
GCHandle user_data);
456+
IntPtr user_data);
457457

458+
// Don't return string for SOUP headers, we don't want to auto-release them (as it's done by LibraryImport)
458459
[LibraryImport(LibSoup, StringMarshalling = StringMarshalling.Utf8)]
459-
public static partial string? soup_message_headers_get_list(IntPtr headers, string name);
460+
public static partial IntPtr soup_message_headers_get_list(IntPtr headers, string name);
460461
[LibraryImport(LibSoup, StringMarshalling = StringMarshalling.Utf8)]
461-
public static partial string? soup_message_headers_get_one(IntPtr headers, string name);
462+
public static partial IntPtr soup_message_headers_get_one(IntPtr headers, string name);
462463
[LibraryImport(LibSoup, StringMarshalling = StringMarshalling.Utf8)]
463464
public static partial void soup_message_headers_replace(IntPtr headers, string name, string value);
464465
[LibraryImport(LibSoup, StringMarshalling = StringMarshalling.Utf8)]
@@ -555,9 +556,11 @@ public struct GdkRectangle
555556
public int width, height;
556557
}
557558

559+
[Flags]
558560
public enum GConnectFlags : int
559561
{
560-
AFTER,
561-
SWAPPED
562+
NONE = 0,
563+
AFTER = 1 << 0,
564+
SWAPPED = 1 << 1
562565
}
563566
}

src/Avalonia.Controls.WebView.Core/Gtk/GtkOffscreenAvaloniaWebViewAdapter.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,19 @@ private GtkOffscreenAvaloniaWebViewAdapter(GtkWebViewEnvironmentRequestedEventAr
3131

3232
public Control? Parent { get; private set; }
3333

34-
public static async Task<WebViewAdapter.OffscreenWebViewAdapterBuilder> CreateBuilder(
34+
public static Task<WebViewAdapter.OffscreenWebViewAdapterBuilder> CreateBuilder(
3535
GtkWebViewEnvironmentRequestedEventArgs environmentArgs)
3636
{
37-
var adapter = await RunOnGlibThreadAsync(() => new GtkOffscreenAvaloniaWebViewAdapter(environmentArgs));
38-
return (parent) =>
37+
// A fresh adapter per attachment: the host disposes the previous one when the control is
38+
// detached, so handing out a cached instance leaves a dead web view after a re-attach.
39+
WebViewAdapter.OffscreenWebViewAdapterBuilder builder = async parent =>
3940
{
41+
var adapter = await RunOnGlibThreadAsync(() => new GtkOffscreenAvaloniaWebViewAdapter(environmentArgs));
4042
adapter.Parent = parent;
41-
return Task.FromResult<IWebViewAdapterWithOffscreenBuffer>(adapter);
43+
return adapter;
4244
};
45+
46+
return Task.FromResult(builder);
4347
}
4448

4549
protected override void DisposeSafe(bool disposing)

src/Avalonia.Controls.WebView.Core/Gtk/GtkOffscreenWebViewAdapter.cs

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ protected GtkOffscreenWebViewAdapter(GtkWebViewEnvironmentRequestedEventArgs arg
3838
}
3939

4040
public event Action? DrawRequested;
41-
41+
42+
public PixelFormat BufferPixelFormat => PixelFormats.Rgba8888;
43+
public AlphaFormat BufferAlphaFormat => AlphaFormat.Unpremul;
44+
4245
public Task UpdateWriteableBitmap(PixelSize _, FrameChainBase<WriteableBitmap, PixelSize>.IProducer producer)
4346
{
4447
if (_windowHandle == IntPtr.Zero)
@@ -65,11 +68,6 @@ public Task UpdateWriteableBitmap(PixelSize _, FrameChainBase<WriteableBitmap, P
6568
int wHeight = gtk_widget_get_allocated_height(_windowHandle);
6669

6770
pixbuf = gdk_pixbuf_get_from_window(gdkWindow, 0, 0, wWidth, wHeight);
68-
if (pixbuf != IntPtr.Zero && gdk_pixbuf_get_n_channels(pixbuf) == 3)
69-
{
70-
var pixbufRgba = gdk_pixbuf_add_alpha(pixbuf, false, 0, 0, 0);
71-
pixbuf = pixbufRgba;
72-
}
7371
}
7472

7573
if (pixbuf == IntPtr.Zero)
@@ -79,46 +77,73 @@ public Task UpdateWriteableBitmap(PixelSize _, FrameChainBase<WriteableBitmap, P
7977

8078
try
8179
{
80+
// Windows without an alpha channel produce a three channel pixbuf.
81+
if (gdk_pixbuf_get_n_channels(pixbuf) == 3)
82+
{
83+
var pixbufRgba = gdk_pixbuf_add_alpha(pixbuf, false, 0, 0, 0);
84+
g_object_unref(pixbuf);
85+
pixbuf = pixbufRgba;
86+
87+
if (pixbuf == IntPtr.Zero)
88+
{
89+
return;
90+
}
91+
}
92+
8293
var width = gdk_pixbuf_get_width(pixbuf);
8394
var height = gdk_pixbuf_get_height(pixbuf);
8495
var stride = gdk_pixbuf_get_rowstride(pixbuf);
8596
var channels = gdk_pixbuf_get_n_channels(pixbuf);
8697
var pixelsPtr = gdk_pixbuf_get_pixels(pixbuf);
8798

99+
if (width <= 0 || height <= 0 || channels != 4 || pixelsPtr == IntPtr.Zero)
100+
{
101+
return;
102+
}
103+
88104
var size = new PixelSize(width, height);
89105

90-
if (channels == 4)
106+
using (producer.GetNextFrame(size, out var frame))
91107
{
92-
using (producer.GetNextFrame(size, out var frame))
108+
using var buf = frame.Lock();
109+
var dstStride = buf.RowBytes;
110+
111+
if (stride == dstStride)
93112
{
94-
using var buf = frame.Lock();
95-
var bytesPerRow = Math.Min(stride, buf.RowBytes);
96-
var totalBytes = bytesPerRow * height;
97-
98-
Buffer.MemoryCopy(
99-
source: (void*)pixelsPtr,
100-
destination: (void*)buf.Address,
101-
destinationSizeInBytes: buf.RowBytes * height,
102-
sourceBytesToCopy: totalBytes
103-
);
113+
Buffer.MemoryCopy((void*)pixelsPtr, (void*)buf.Address,
114+
(long)height * dstStride, (long)height * stride);
115+
}
116+
else
117+
{
118+
var copyBytes = Math.Min(stride, dstStride);
119+
for (var y = 0; y < height; y++)
120+
{
121+
Buffer.MemoryCopy(
122+
(byte*)pixelsPtr + (long)y * stride,
123+
(byte*)buf.Address + (long)y * dstStride,
124+
dstStride, copyBytes);
125+
}
104126
}
105127
}
106128
}
107129
finally
108130
{
109-
g_object_unref(pixbuf);
131+
if (pixbuf != IntPtr.Zero)
132+
{
133+
g_object_unref(pixbuf);
134+
}
110135
}
111136
});
112137
}
113138

114139
public override void SizeChanged(PixelSize containerSize)
115140
{
116-
if (_windowHandle == IntPtr.Zero)
117-
return;
118-
119141
_sizeRequest = containerSize;
120142
RunOnGlibThreadAsync(() =>
121143
{
144+
if (_windowHandle == IntPtr.Zero)
145+
return;
146+
122147
if (_experimentalOffscreen)
123148
gtk_window_set_default_size(_windowHandle, _sizeRequest.Width, _sizeRequest.Height);
124149
else
@@ -300,8 +325,10 @@ protected override void DisposeSafe(bool disposing)
300325

301326
if (window != IntPtr.Zero)
302327
{
303-
g_object_unref(window);
328+
// Destroy before releasing our reference: unreffing first can drop the last reference,
329+
// and gtk_widget_destroy would then run against freed memory.
304330
gtk_widget_destroy(window);
331+
g_object_unref(window);
305332
}
306333
}
307334

0 commit comments

Comments
 (0)