1package webkit
2
3/*
4#cgo pkg-config: webkitgtk-6.0
5#include <webkit/webkit.h>
6*/
7import "C"
8
9import (
10 "context"
11 "crypto/md5"
12 "fmt"
13 "net/url"
14 "runtime"
15 "unsafe"
16
17 webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6"
18 "github.com/diamondburned/gotk4/pkg/core/gerror"
19 coreglib "github.com/diamondburned/gotk4/pkg/core/glib"
20 "github.com/diamondburned/gotk4/pkg/gdk/v4"
21 "github.com/diamondburned/gotk4/pkg/gdkpixbuf/v2"
22 gio "github.com/diamondburned/gotk4/pkg/gio/v2"
23 "github.com/diamondburned/gotk4/pkg/glib/v2"
24)
25
26// GdkTexture is an alias for gotk4's Texture type
27type GdkTexture = gdk.Texture
28
29// FaviconDatabase is an alias for WebKit's FaviconDatabase
30type FaviconDatabase = webkit.FaviconDatabase
31
32// SaveTextureAsPNG saves a GdkTexture as a PNG file at target size
33// WebKit provides GdkTexture which can be any format (ICO, PNG, JPEG, SVG)
34// This function normalizes everything to PNG at the target size (32x32)
35func SaveTextureAsPNG(texture *gdk.Texture, path string, targetSize int) error {
36 if texture == nil {
37 return fmt.Errorf("texture cannot be nil")
38 }
39
40 width := texture.Width()
41 height := texture.Height()
42
43 // If texture needs scaling, create scaled version
44 if width != targetSize || height != targetSize {
45 texture = ScaleTexture(texture, targetSize, targetSize)
46 if texture == nil {
47 return fmt.Errorf("failed to scale texture")
48 }
49 }
50
51 // Save texture as PNG using built-in method
52 if !texture.SaveToPNG(path) {
53 return fmt.Errorf("failed to save PNG to %s", path)
54 }
55
56 return nil
57}
58
59// ScaleTexture scales a texture to the target size using GdkPixbuf
60// Uses high-quality bilinear interpolation
61func ScaleTexture(texture *gdk.Texture, targetWidth, targetHeight int) *gdk.Texture {
62 if texture == nil {
63 return nil
64 }
65
66 // Get PNG bytes from texture
67 pngBytes := texture.SaveToPNGBytes()
68 if pngBytes == nil {
69 return nil
70 }
71
72 // Load as pixbuf using memory input stream
73 ctx := context.Background()
74 stream := gio.NewMemoryInputStreamFromBytes(pngBytes)
75 pixbuf, err := gdkpixbuf.NewPixbufFromStream(ctx, stream)
76 if err != nil || pixbuf == nil {
77 return nil
78 }
79
80 // Scale pixbuf
81 scaled := pixbuf.ScaleSimple(targetWidth, targetHeight, gdkpixbuf.InterpBilinear)
82 if scaled == nil {
83 return nil
84 }
85
86 // Convert back to texture
87 return gdk.NewTextureForPixbuf(scaled)
88}
89
90// HashURL generates an MD5 hash of a URL for filename generation
91func HashURL(pageURL string) []byte {
92 hash := md5.Sum([]byte(pageURL))
93 return hash[:]
94}
95
96// ParseURL is a convenience wrapper around url.Parse
97func ParseURL(rawURL string) (*url.URL, error) {
98 return url.Parse(rawURL)
99}
100
101// FaviconFinishSafe mirrors gotk4's FaviconFinish but guards against NULL returns.
102func FaviconFinishSafe(database *webkit.FaviconDatabase, result gio.AsyncResulter) (*gdk.Texture, error) {
103 if database == nil || result == nil {
104 return nil, fmt.Errorf("favicon finish: invalid arguments")
105 }
106
107 cDatabase := (*C.WebKitFaviconDatabase)(unsafe.Pointer(glib.BaseObject(database).Native()))
108 cResult := (*C.GAsyncResult)(unsafe.Pointer(glib.BaseObject(result).Native()))
109
110 var cErr *C.GError
111 cTexture := C.webkit_favicon_database_get_favicon_finish(cDatabase, cResult, &cErr)
112 runtime.KeepAlive(database)
113 runtime.KeepAlive(result)
114
115 if cErr != nil {
116 err := gerror.Take(unsafe.Pointer(cErr))
117 return nil, err
118 }
119 if cTexture == nil {
120 return nil, nil
121 }
122
123 object := coreglib.AssumeOwnership(unsafe.Pointer(cTexture))
124 casted := object.WalkCast(func(obj coreglib.Objector) bool {
125 _, ok := obj.(gdk.Texturer)
126 return ok
127 })
128
129 texturer, ok := casted.(gdk.Texturer)
130 if !ok {
131 return nil, fmt.Errorf("favicon finish: unexpected type %T", casted)
132 }
133
134 texture := gdk.BaseTexture(texturer)
135 if texture == nil {
136 return nil, fmt.Errorf("favicon finish: texture unavailable")
137 }
138
139 return texture, nil
140}
141
142// TextureFromImageBytes creates a texture from raw image bytes.
143// Supported formats depend on the available GdkPixbuf loaders (PNG, SVG, etc.).
144func TextureFromImageBytes(data []byte) (*gdk.Texture, error) {
145 if len(data) == 0 {
146 return nil, fmt.Errorf("image data is empty")
147 }
148
149 // Convert []byte to glib.Bytes for gio API
150 glibBytes := glib.NewBytes(data)
151 stream := gio.NewMemoryInputStreamFromBytes(glibBytes)
152 pixbuf, err := gdkpixbuf.NewPixbufFromStream(context.Background(), stream)
153 if err != nil {
154 return nil, fmt.Errorf("load pixbuf: %w", err)
155 }
156
157 texture := gdk.NewTextureForPixbuf(pixbuf)
158 if texture == nil {
159 return nil, fmt.Errorf("create texture from pixbuf failed")
160 }
161
162 return texture, nil
163}