gpui: Fix SVG color render, when color have alpha (#20537)

Jason Lee and Floyd Wang created

Release Notes:

- N/A


## Demo

- [Source
SVG](https://github.com/user-attachments/assets/1c681e01-baba-4613-a3e7-ea5cb3015406)
click here open in browser.

| Before | After |
| --- | --- |
| <img width="1212" alt="image"
src="https://github.com/user-attachments/assets/ba323b13-538b-4a34-bb64-9dcf490aface">
| <img width="1212" alt="image"
src="https://github.com/user-attachments/assets/4635926a-843e-426d-89a1-4e9b4f4cc37e">
|

---------

Co-authored-by: Floyd Wang <gassnake999@gmail.com>

Change summary

crates/gpui/examples/image/color.svg        |  6 +++---
crates/gpui/src/color.rs                    | 11 +++++++++++
crates/gpui/src/elements/img.rs             | 11 +++++------
crates/gpui/src/platform/mac/text_system.rs | 13 +++++--------
4 files changed, 24 insertions(+), 17 deletions(-)

Detailed changes

crates/gpui/examples/image/color.svg 🔗

@@ -6,8 +6,8 @@
     <circle cx="240" cy="100" r="30" stroke="#dc2626" />
     <circle cx="380" cy="100" r="20" stroke="#d97706" />
     <circle cx="380" cy="240" r="30" stroke="#06b6d4" />
-    <circle cx="100" cy="240" r="30" stroke="#3b82f6" />
+    <circle cx="100" cy="240" r="30" stroke="#3b82f666" />
     <circle cx="240" cy="380" r="30" stroke="#7c3aed" />
     <circle cx="380" cy="380" r="20" stroke="#c026d3" />
-    <circle cx="100" cy="380" r="20" stroke="#e11d48" />
-</svg>
+    <circle cx="100" cy="380" r="20" stroke="#e11d4866" />
+</svg>

crates/gpui/src/color.rs 🔗

@@ -22,6 +22,17 @@ pub fn rgba(hex: u32) -> Rgba {
     Rgba { r, g, b, a }
 }
 
+/// Swap from RGBA with premultiplied alpha to BGRA
+pub(crate) fn swap_rgba_pa_to_bgra(color: &mut [u8]) {
+    color.swap(0, 2);
+    if color[3] > 0 {
+        let a = color[3] as f32 / 255.;
+        color[0] = (color[0] as f32 / a) as u8;
+        color[1] = (color[1] as f32 / a) as u8;
+        color[2] = (color[2] as f32 / a) as u8;
+    }
+}
+
 /// An RGBA color
 #[derive(PartialEq, Clone, Copy, Default)]
 pub struct Rgba {

crates/gpui/src/elements/img.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{
-    px, AbsoluteLength, AnyElement, AppContext, Asset, AssetLogger, Bounds, DefiniteLength,
-    Element, ElementId, GlobalElementId, Hitbox, Image, InteractiveElement, Interactivity,
-    IntoElement, LayoutId, Length, ObjectFit, Pixels, RenderImage, Resource, SharedString,
-    SharedUri, StyleRefinement, Styled, SvgSize, Task, WindowContext,
+    px, swap_rgba_pa_to_bgra, AbsoluteLength, AnyElement, AppContext, Asset, AssetLogger, Bounds,
+    DefiniteLength, Element, ElementId, GlobalElementId, Hitbox, Image, InteractiveElement,
+    Interactivity, IntoElement, LayoutId, Length, ObjectFit, Pixels, RenderImage, Resource,
+    SharedString, SharedUri, StyleRefinement, Styled, SvgSize, Task, WindowContext,
 };
 use anyhow::{anyhow, Result};
 
@@ -564,9 +564,8 @@ impl Asset for ImageAssetLoader {
                 let mut buffer =
                     ImageBuffer::from_raw(pixmap.width(), pixmap.height(), pixmap.take()).unwrap();
 
-                // Convert from RGBA to BGRA.
                 for pixel in buffer.chunks_exact_mut(4) {
-                    pixel.swap(0, 2);
+                    swap_rgba_pa_to_bgra(pixel);
                 }
 
                 RenderImage::new(SmallVec::from_elem(Frame::new(buffer), 1))

crates/gpui/src/platform/mac/text_system.rs 🔗

@@ -1,7 +1,8 @@
 use crate::{
-    point, px, size, Bounds, DevicePixels, Font, FontFallbacks, FontFeatures, FontId, FontMetrics,
-    FontRun, FontStyle, FontWeight, GlyphId, LineLayout, Pixels, PlatformTextSystem, Point,
-    RenderGlyphParams, Result, ShapedGlyph, ShapedRun, SharedString, Size, SUBPIXEL_VARIANTS,
+    point, px, size, swap_rgba_pa_to_bgra, Bounds, DevicePixels, Font, FontFallbacks, FontFeatures,
+    FontId, FontMetrics, FontRun, FontStyle, FontWeight, GlyphId, LineLayout, Pixels,
+    PlatformTextSystem, Point, RenderGlyphParams, Result, ShapedGlyph, ShapedRun, SharedString,
+    Size, SUBPIXEL_VARIANTS,
 };
 use anyhow::anyhow;
 use cocoa::appkit::CGFloat;
@@ -418,11 +419,7 @@ impl MacTextSystemState {
             if params.is_emoji {
                 // Convert from RGBA with premultiplied alpha to BGRA with straight alpha.
                 for pixel in bytes.chunks_exact_mut(4) {
-                    pixel.swap(0, 2);
-                    let a = pixel[3] as f32 / 255.;
-                    pixel[0] = (pixel[0] as f32 / a) as u8;
-                    pixel[1] = (pixel[1] as f32 / a) as u8;
-                    pixel[2] = (pixel[2] as f32 / a) as u8;
+                    swap_rgba_pa_to_bgra(pixel);
                 }
             }