Просмотр исходного кода

wayland: Scale non-square icon images

Wayland icons must be square, so scale non-square images to fit a square region instead of failing. This matches X11 behavior.

A warning that the image will be scaled will be logged.
Frank Praznik 2 месяцев назад
Родитель
Сommit
4fcb92e0df
1 измененных файлов с 48 добавлено и 32 удалено
  1. 48 32
      src/video/wayland/SDL_waylandwindow.c

+ 48 - 32
src/video/wayland/SDL_waylandwindow.c

@@ -3003,10 +3003,6 @@ bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surfa
         return SDL_SetError("wayland: cannot set icon; required xdg_toplevel_icon_v1 protocol not supported");
     }
 
-    if (icon->w != icon->h) {
-        return SDL_SetError("wayland: icon width and height must be equal, got %ix%i", icon->w, icon->h);
-    }
-
     int image_count = 0;
     SDL_Surface **images = SDL_GetSurfaceImages(icon, &image_count);
     if (!images || !image_count) {
@@ -3034,11 +3030,12 @@ bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surfa
     size_t pool_size = 0;
     for (int i = 0; i < image_count; ++i) {
         // Ignore non-square images; if we got here, we know that at least the base image is square.
-        if (images[i]->w == images[i]->h) {
-            pool_size += images[i]->w * images[i]->h * 4;
-        } else {
-            SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "wayland: icon width and height must be equal, got %ix%i for image level %i; skipping", images[i]->w, images[i]->h, i);
+        if (images[i]->w != images[i]->h) {
+            SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "wayland: icon width and height must be equal, got %ix%i for image level %i; icon will be scaled", images[i]->w, images[i]->h, i);
         }
+
+        const int size = SDL_max(images[i]->w, images[i]->h);
+        pool_size += size * size * 4;
     }
 
     // Sort the images in ascending order by size.
@@ -3046,46 +3043,65 @@ bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surfa
 
     shm_pool = Wayland_AllocSHMPool(pool_size);
     if (!shm_pool) {
-        SDL_SetError("wayland: failed to allocate SHM pool for the icon");
+        SDL_SetError("wayland: failed to allocate an SHM pool for the icon");
         goto failure_cleanup;
     }
 
+    const double base_size = (double)SDL_max(icon->w, icon->h);
     for (int i = 0; i < image_count; ++i) {
-        if (images[i]->w == images[i]->h) {
-            SDL_Surface *surface = images[i];
+        SDL_Surface *surface = images[i];
 
-            // Choose the largest image for each integer scale, ignoring any below the base size.
-            const int scale = (int)SDL_floor((double)surface->w / (double)icon->w);
-            if (!scale) {
-                continue;
-            }
+        // Choose the largest image for each integer scale, ignoring any below the base size.
+        const int level_size = SDL_max(surface->w, surface->h);
+        const int scale = (int)SDL_floor((double)level_size / base_size);
+        if (!scale) {
+            continue;
+        }
 
-            void *buffer_mem;
-            struct wl_buffer *buffer = Wayland_AllocBufferFromPool(shm_pool, surface->w, surface->h, &buffer_mem);
-            if (!buffer) {
-                SDL_SetError("wayland: failed to allocate SHM buffer for the icon");
+        if (surface->format != SDL_PIXELFORMAT_ARGB8888) {
+            surface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
+            if (!surface) {
+                SDL_SetError("wayland: failed to convert the icon image to ARGB8888 format");
                 goto failure_cleanup;
             }
+        }
 
-            wind->icon_buffers[wind->icon_buffer_count++] = buffer;
+        if (surface->w != surface->h) {
+            SDL_Surface *scaled_surface = SDL_ScaleSurface(surface, level_size, level_size, SDL_SCALEMODE_LINEAR);
 
-            if (surface->format != SDL_PIXELFORMAT_ARGB8888) {
-                surface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
-                if (!surface) {
-                    SDL_SetError("wayland: unable to convert surface to ARGB8888 format");
-                    goto failure_cleanup;
-                }
+            // Clean up the temporary conversion surface.
+            if (surface != images[i]) {
+                SDL_DestroySurface(surface);
             }
+            surface = scaled_surface;
 
-            SDL_PremultiplyAlpha(surface->w, surface->h, surface->format, surface->pixels, surface->pitch,
-                                 SDL_PIXELFORMAT_ARGB8888, buffer_mem, surface->w * 4, true);
-
-            xdg_toplevel_icon_v1_add_buffer(wind->xdg_toplevel_icon_v1, buffer, scale);
+            if (!surface) {
+                SDL_SetError("wayland: failed to scale the non-square icon image to the required size");
+                goto failure_cleanup;
+            }
+        }
 
-            // Clean up the temporary conversion surface.
+        void *buffer_mem;
+        struct wl_buffer *buffer = Wayland_AllocBufferFromPool(shm_pool, surface->w, surface->h, &buffer_mem);
+        if (!buffer) {
+            // Clean up the temporary scaled or conversion surface.
             if (surface != images[i]) {
                 SDL_DestroySurface(surface);
             }
+            SDL_SetError("wayland: failed to allocate a wl_buffer for the icon");
+            goto failure_cleanup;
+        }
+
+        wind->icon_buffers[wind->icon_buffer_count++] = buffer;
+
+        SDL_PremultiplyAlpha(surface->w, surface->h, surface->format, surface->pixels, surface->pitch,
+                             SDL_PIXELFORMAT_ARGB8888, buffer_mem, surface->w * 4, true);
+
+        xdg_toplevel_icon_v1_add_buffer(wind->xdg_toplevel_icon_v1, buffer, scale);
+
+        // Clean up the temporary scaled or conversion surface.
+        if (surface != images[i]) {
+            SDL_DestroySurface(surface);
         }
     }