Merge "DO NOT MERGE - Merge Android 13"
diff --git a/minui/graphics.cpp b/minui/graphics.cpp
index 597f2ce..b24c2b1 100644
--- a/minui/graphics.cpp
+++ b/minui/graphics.cpp
@@ -36,7 +36,6 @@
 static int overscan_offset_y = 0;
 
 static uint32_t gr_current = ~0;
-static constexpr uint32_t alpha_mask = 0xff000000;
 
 // gr_draw is owned by backends.
 static GRSurface* gr_draw = nullptr;
@@ -79,7 +78,7 @@
 }
 
 // Blends gr_current onto pix value, assumes alpha as most significant byte.
-static inline uint32_t pixel_blend(uint8_t alpha, uint32_t pix) {
+static inline uint32_t pixel_blend_argb(uint8_t alpha, uint32_t pix) {
   if (alpha == 255) return gr_current;
   if (alpha == 0) return pix;
   uint32_t pix_r = pix & 0xff;
@@ -96,6 +95,48 @@
   return (out_r & 0xff) | (out_g & 0xff00) | (out_b & 0xff0000) | (gr_current & 0xff000000);
 }
 
+static inline uint32_t pixel_blend_rgba(uint8_t alpha, uint32_t pix) {
+  if (alpha == 255) return gr_current;
+  if (alpha == 0) return pix;
+  uint32_t pix_r = pix & 0xff00;
+  uint32_t pix_g = pix & 0xff0000;
+  uint32_t pix_b = pix & 0xff000000;
+  uint32_t cur_r = gr_current & 0xff00;
+  uint32_t cur_g = gr_current & 0xff0000;
+  uint32_t cur_b = gr_current & 0xff000000;
+
+  uint32_t out_r = (pix_r * (255 - alpha) + cur_r * alpha) / 255;
+  uint32_t out_g = (pix_g * (255 - alpha) + cur_g * alpha) / 255;
+  uint32_t out_b = (pix_b * (255 - alpha) + cur_b * alpha) / 255;
+
+  return (gr_current & 0xff) | (out_r & 0xff00) | (out_g & 0xff0000) | (out_b & 0xff000000);
+}
+
+static inline uint32_t pixel_blend(uint8_t alpha, uint32_t pix) {
+  if (pixel_format == PixelFormat::RGBA) {
+    return pixel_blend_rgba(alpha, pix);
+  }
+  return pixel_blend_argb(alpha, pix);
+}
+
+static inline uint32_t get_alphamask() {
+  if (pixel_format == PixelFormat::RGBA) {
+    return 0x000000ff;
+  }
+  return 0xff000000;
+}
+
+static inline uint8_t get_alpha_shift() {
+  if (pixel_format == PixelFormat::RGBA) {
+    return 0;
+  }
+  return 24;
+}
+
+static inline uint8_t get_alpha(uint32_t pix) {
+  return static_cast<uint8_t>((pix & (gr_current & get_alphamask())) >> get_alpha_shift());
+}
+
 // Increments pixel pointer right, with current rotation.
 static void incr_x(uint32_t** p, int row_pixels) {
   if (rotation == GRRotation::LEFT) {
@@ -143,7 +184,7 @@
 
 static void TextBlend(const uint8_t* src_p, int src_row_bytes, uint32_t* dst_p, int dst_row_pixels,
                       int width, int height) {
-  uint8_t alpha_current = static_cast<uint8_t>((alpha_mask & gr_current) >> 24);
+  uint8_t alpha_current = get_alpha(gr_current);
   for (int j = 0; j < height; ++j) {
     const uint8_t* sx = src_p;
     uint32_t* px = dst_p;
@@ -158,7 +199,7 @@
 }
 
 void gr_text(const GRFont* font, int x, int y, const char* s, bool bold) {
-  if (!font || !font->texture || (gr_current & alpha_mask) == 0) return;
+  if (!font || !font->texture || (gr_current & get_alphamask()) == 0) return;
 
   if (font->texture->pixel_bytes != 1) {
     printf("gr_text: font has wrong format\n");
@@ -213,6 +254,8 @@
   uint32_t r32 = r, g32 = g, b32 = b, a32 = a;
   if (pixel_format == PixelFormat::ARGB || pixel_format == PixelFormat::BGRA) {
     gr_current = (a32 << 24) | (r32 << 16) | (g32 << 8) | b32;
+  } else if (pixel_format == PixelFormat::RGBA) {
+    gr_current = (b32 << 24) | (g32 << 16) | (r32 << 8) | a32;
   } else {
     gr_current = (a32 << 24) | (b32 << 16) | (g32 << 8) | r32;
   }
@@ -247,7 +290,7 @@
 
   int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes;
   uint32_t* p = PixelAt(gr_draw, x1, y1, row_pixels);
-  uint8_t alpha = static_cast<uint8_t>(((gr_current & alpha_mask) >> 24));
+  uint8_t alpha = get_alpha(gr_current);
   if (alpha > 0) {
     for (int y = y1; y < y2; ++y) {
       uint32_t* px = p;
@@ -369,6 +412,8 @@
     pixel_format = PixelFormat::ARGB;
   } else if (format == "BGRA_8888") {
     pixel_format = PixelFormat::BGRA;
+  } else if (format == "RGBA_8888") {
+    pixel_format = PixelFormat::RGBA;
   } else {
     pixel_format = PixelFormat::UNKNOWN;
   }
diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h
index a2f62f0..f9be82f 100644
--- a/minui/include/minui/minui.h
+++ b/minui/include/minui/minui.h
@@ -102,6 +102,7 @@
   RGBX = 2,
   BGRA = 3,
   ARGB = 4,
+  RGBA = 5, // LSB Alpha
 };
 
 enum class GraphicsBackend : int {
diff --git a/minui/resources.cpp b/minui/resources.cpp
index d7b9277..1521c8f 100644
--- a/minui/resources.cpp
+++ b/minui/resources.cpp
@@ -153,32 +153,57 @@
                                int width) {
   const uint8_t* ip = input_row;
   uint8_t* op = output_row;
+  PixelFormat pixel_format = gr_pixel_format();
 
   switch (channels) {
     case 1:
       // expand gray level to RGBX
       for (int x = 0; x < width; ++x) {
-        *op++ = *ip;
-        *op++ = *ip;
-        *op++ = *ip;
-        *op++ = 0xff;
+        if (pixel_format == PixelFormat::RGBA) {
+          *op++ = 0xff;
+          *op++ = *ip;
+          *op++ = *ip;
+          *op++ = *ip;
+        } else {
+          *op++ = *ip;
+          *op++ = *ip;
+          *op++ = *ip;
+          *op++ = 0xff;
+        }
         ip++;
       }
       break;
 
     case 3:
-      // expand RGBA to RGBX
       for (int x = 0; x < width; ++x) {
-        *op++ = *ip++;
-        *op++ = *ip++;
-        *op++ = *ip++;
-        *op++ = 0xff;
+        // expand RGBA to RGBX
+        if (pixel_format == PixelFormat::RGBA) {
+            *op++ = 0xff;
+            *op++ = *ip++;
+            *op++ = *ip++;
+            *op++ = *ip++;
+        } else {
+            *op++ = *ip++;
+            *op++ = *ip++;
+            *op++ = *ip++;
+            *op++ = 0xff;
+        }
       }
       break;
 
     case 4:
-      // copy RGBA to RGBX
-      memcpy(output_row, input_row, width * 4);
+      if (pixel_format == PixelFormat::RGBA) {
+        for (int x = 0; x < width; ++x) {
+            *op++ = *(ip + 3);
+            *op++ = *ip++;
+            *op++ = *ip++;
+            *op++ = *ip++;
+            ip++;
+        }
+      } else {
+        // copy RGBA to RGBX
+        memcpy(output_row, input_row, width * 4);
+      }
       break;
   }
 }
@@ -201,6 +226,8 @@
   PixelFormat pixel_format = gr_pixel_format();
   if (pixel_format == PixelFormat::ARGB || pixel_format == PixelFormat::BGRA) {
     png_set_bgr(png_ptr);
+  } else if (pixel_format == PixelFormat::RGBA) {
+    png_set_swap_alpha(png_ptr);
   }
 
   for (png_uint_32 y = 0; y < height; ++y) {
@@ -273,6 +300,8 @@
 
   if (gr_pixel_format() == PixelFormat::ARGB || gr_pixel_format() == PixelFormat::BGRA) {
     png_set_bgr(png_ptr);
+  } else if (gr_pixel_format() == PixelFormat::RGBA) {
+    png_set_swap_alpha(png_ptr);
   }
 
   for (png_uint_32 y = 0; y < height; ++y) {
@@ -316,11 +345,6 @@
     return -8;
   }
 
-  PixelFormat pixel_format = gr_pixel_format();
-  if (pixel_format == PixelFormat::ARGB || pixel_format == PixelFormat::BGRA) {
-    png_set_bgr(png_ptr);
-  }
-
   for (png_uint_32 y = 0; y < height; ++y) {
     uint8_t* p_row = surface->data() + y * surface->row_bytes;
     png_read_row(png_ptr, p_row, nullptr);