pixman-renderer: track Pixman formats in pixel format table

Adds a Pixman format field to the pixel format table, and
adjusts the shm format handling code in the Pixman renderer
to use this table.

Pixman formats have been registered only for specific 565, 8888,
and 2101010 layouts, as these have corresponding DRM format codes
and are commonly used.

Signed-off-by: Manuel Stoeckl <code@mstoeckl.com>
diff --git a/libweston/pixel-formats.c b/libweston/pixel-formats.c
index 9c19a55..9064cb6 100644
--- a/libweston/pixel-formats.c
+++ b/libweston/pixel-formats.c
@@ -65,6 +65,8 @@
 	.bits.a = a_, \
 	.component_type = PIXEL_COMPONENT_TYPE_FIXED
 
+#define PIXMAN_FMT(fmt) .pixman_format = (PIXMAN_ ## fmt)
+
 #include "shared/weston-egl-ext.h"
 
 /**
@@ -171,6 +173,7 @@
 # if __BYTE_ORDER == __LITTLE_ENDIAN
 		GL_FORMAT(GL_RGB),
 		GL_TYPE(GL_UNSIGNED_SHORT_5_6_5),
+		PIXMAN_FMT(r5g6b5),
 #endif
 	},
 	{
@@ -194,6 +197,11 @@
 		.bpp = 32,
 		GL_FORMAT(GL_BGRA_EXT),
 		GL_TYPE(GL_UNSIGNED_BYTE),
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+		PIXMAN_FMT(x8r8g8b8),
+#else
+		PIXMAN_FMT(b8g8r8x8),
+#endif
 	},
 	{
 		DRM_FORMAT(ARGB8888),
@@ -203,12 +211,22 @@
 		.bpp = 32,
 		GL_FORMAT(GL_BGRA_EXT),
 		GL_TYPE(GL_UNSIGNED_BYTE),
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+		PIXMAN_FMT(a8r8g8b8),
+#else
+		PIXMAN_FMT(b8g8r8a8),
+#endif
 	},
 	{
 		DRM_FORMAT(XBGR8888),
 		BITS_RGBA_FIXED(8, 8, 8, 0),
 		GL_FORMAT(GL_RGBA),
 		GL_TYPE(GL_UNSIGNED_BYTE),
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+		PIXMAN_FMT(x8b8g8r8),
+#else
+		PIXMAN_FMT(r8g8b8x8),
+#endif
 	},
 	{
 		DRM_FORMAT(ABGR8888),
@@ -216,35 +234,66 @@
 		.opaque_substitute = DRM_FORMAT_XBGR8888,
 		GL_FORMAT(GL_RGBA),
 		GL_TYPE(GL_UNSIGNED_BYTE),
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+		PIXMAN_FMT(a8b8g8r8),
+#else
+		PIXMAN_FMT(r8g8b8a8),
+#endif
 	},
 	{
 		DRM_FORMAT(RGBX8888),
 		BITS_RGBA_FIXED(8, 8, 8, 0),
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+		PIXMAN_FMT(r8g8b8x8),
+#else
+		PIXMAN_FMT(x8b8g8r8),
+#endif
 	},
 	{
 		DRM_FORMAT(RGBA8888),
 		BITS_RGBA_FIXED(8, 8, 8, 8),
 		.opaque_substitute = DRM_FORMAT_RGBX8888,
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+		PIXMAN_FMT(r8g8b8a8),
+#else
+		PIXMAN_FMT(a8b8g8r8),
+#endif
 	},
 	{
 		DRM_FORMAT(BGRX8888),
 		BITS_RGBA_FIXED(8, 8, 8, 0),
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+		PIXMAN_FMT(b8g8r8x8),
+#else
+		PIXMAN_FMT(x8r8g8b8),
+#endif
 	},
 	{
 		DRM_FORMAT(BGRA8888),
 		BITS_RGBA_FIXED(8, 8, 8, 8),
 		.opaque_substitute = DRM_FORMAT_BGRX8888,
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+		PIXMAN_FMT(b8g8r8a8),
+#else
+		PIXMAN_FMT(a8r8g8b8),
+#endif
 	},
 	{
 		DRM_FORMAT(XRGB2101010),
 		BITS_RGBA_FIXED(10, 10, 10, 0),
 		.depth = 30,
 		.bpp = 32,
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+		PIXMAN_FMT(x2r10g10b10),
+#endif
 	},
 	{
 		DRM_FORMAT(ARGB2101010),
 		BITS_RGBA_FIXED(10, 10, 10, 2),
 		.opaque_substitute = DRM_FORMAT_XRGB2101010,
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+		PIXMAN_FMT(a2r10g10b10),
+#endif
 	},
 	{
 		DRM_FORMAT(XBGR2101010),
@@ -252,6 +301,7 @@
 # if __BYTE_ORDER == __LITTLE_ENDIAN
 		GL_FORMAT(GL_RGBA),
 		GL_TYPE(GL_UNSIGNED_INT_2_10_10_10_REV_EXT),
+		PIXMAN_FMT(x2b10g10r10),
 #endif
 	},
 	{
@@ -261,6 +311,7 @@
 # if __BYTE_ORDER == __LITTLE_ENDIAN
 		GL_FORMAT(GL_RGBA),
 		GL_TYPE(GL_UNSIGNED_INT_2_10_10_10_REV_EXT),
+		PIXMAN_FMT(a2b10g10r10),
 #endif
 	},
 	{
@@ -448,6 +499,22 @@
 }
 
 WL_EXPORT const struct pixel_format_info *
+pixel_format_get_info_by_index(unsigned int index)
+{
+	if (index >= ARRAY_LENGTH(pixel_format_table))
+		return NULL;
+
+	return &pixel_format_table[index];
+}
+
+WL_EXPORT unsigned int
+pixel_format_get_info_count(void)
+{
+	return ARRAY_LENGTH(pixel_format_table);
+}
+
+
+WL_EXPORT const struct pixel_format_info *
 pixel_format_get_info_by_drm_name(const char *drm_format_name)
 {
 	const struct pixel_format_info *info;
diff --git a/libweston/pixel-formats.h b/libweston/pixel-formats.h
index d96bcfb..0b1a5f5 100644
--- a/libweston/pixel-formats.h
+++ b/libweston/pixel-formats.h
@@ -26,6 +26,7 @@
 
 #include <inttypes.h>
 #include <stdbool.h>
+#include <pixman.h>
 
 /**
  * Contains information about pixel formats, mapping format codes from
@@ -70,6 +71,9 @@
 	/** GL data type, if data can be natively/directly uploaded. */
 	int gl_type;
 
+	/** Pixman data type, if it agrees exactly with the wl_shm format */
+	pixman_format_code_t pixman_format;
+
 	/** If set, this format can be used with the legacy drmModeAddFB()
 	 *  function (not AddFB2), using this and the bpp member. */
 	int depth;
@@ -143,6 +147,27 @@
 pixel_format_get_info_shm(uint32_t format);
 
 /**
+ * Get pixel format information by table index
+ *
+ * Given a 0-based index in the format table, return the corresponding
+ * DRM pixel format info structure.
+ *
+ * @param index Index of the pixel format in the table
+ * @returns A pixel format structure (must not be freed), or NULL if the
+ *          index is out of range.
+ */
+const struct pixel_format_info *
+pixel_format_get_info_by_index(unsigned int index);
+
+/**
+ * Return the size of the pixel format table
+ *
+ * @returns The number of entries in the pixel format table
+ */
+unsigned int
+pixel_format_get_info_count(void);
+
+/**
  * Get pixel format information for a named DRM format
  *
  * Given a DRM format name, return a pixel format info structure describing
diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c
index ebc0fa7..754adce 100644
--- a/libweston/pixman-renderer.c
+++ b/libweston/pixman-renderer.c
@@ -34,6 +34,7 @@
 
 #include "pixman-renderer.h"
 #include "color.h"
+#include "pixel-formats.h"
 #include "shared/helpers.h"
 
 #include <linux/input.h>
@@ -623,7 +624,7 @@
 {
 	struct pixman_surface_state *ps = get_surface_state(es);
 	struct wl_shm_buffer *shm_buffer;
-	pixman_format_code_t pixman_format;
+	const struct pixel_format_info *pixel_info;
 
 	weston_buffer_reference(&ps->buffer_ref, buffer);
 	weston_buffer_release_reference(&ps->buffer_release_ref,
@@ -651,20 +652,8 @@
 		return;
 	}
 
-	switch (wl_shm_buffer_get_format(shm_buffer)) {
-	case WL_SHM_FORMAT_XRGB8888:
-		pixman_format = PIXMAN_x8r8g8b8;
-		es->is_opaque = true;
-		break;
-	case WL_SHM_FORMAT_ARGB8888:
-		pixman_format = PIXMAN_a8r8g8b8;
-		es->is_opaque = false;
-		break;
-	case WL_SHM_FORMAT_RGB565:
-		pixman_format = PIXMAN_r5g6b5;
-		es->is_opaque = true;
-		break;
-	default:
+	pixel_info = pixel_format_get_info_shm(wl_shm_buffer_get_format(shm_buffer));
+	if (!pixel_info || !pixman_format_supported_source(pixel_info->pixman_format)) {
 		weston_log("Unsupported SHM buffer format 0x%x\n",
 			wl_shm_buffer_get_format(shm_buffer));
 		weston_buffer_reference(&ps->buffer_ref, NULL);
@@ -672,14 +661,15 @@
 		weston_buffer_send_server_error(buffer,
 			"disconnecting due to unhandled buffer type");
 		return;
-	break;
 	}
 
+	es->is_opaque = pixel_format_is_opaque(pixel_info);
+
 	buffer->shm_buffer = shm_buffer;
 	buffer->width = wl_shm_buffer_get_width(shm_buffer);
 	buffer->height = wl_shm_buffer_get_height(shm_buffer);
 
-	ps->image = pixman_image_create_bits(pixman_format,
+	ps->image = pixman_image_create_bits(pixel_info->pixman_format,
 		buffer->width, buffer->height,
 		wl_shm_buffer_get_data(shm_buffer),
 		wl_shm_buffer_get_stride(shm_buffer));
@@ -864,6 +854,8 @@
 pixman_renderer_init(struct weston_compositor *ec)
 {
 	struct pixman_renderer *renderer;
+	const struct pixel_format_info *pixel_info, *info_argb8888, *info_xrgb8888;
+	unsigned int i, num_formats;
 
 	renderer = zalloc(sizeof *renderer);
 	if (renderer == NULL)
@@ -889,7 +881,21 @@
 		weston_compositor_add_debug_binding(ec, KEY_R,
 						    debug_binding, ec);
 
-	wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_RGB565);
+	info_argb8888 = pixel_format_get_info_shm(WL_SHM_FORMAT_ARGB8888);
+	info_xrgb8888 = pixel_format_get_info_shm(WL_SHM_FORMAT_XRGB8888);
+
+	num_formats = pixel_format_get_info_count();
+	for (i = 0; i < num_formats; i++) {
+		pixel_info = pixel_format_get_info_by_index(i);
+		if (!pixman_format_supported_source(pixel_info->pixman_format))
+			continue;
+
+		/* skip formats which libwayland registers by default */
+		if (pixel_info == info_argb8888 || pixel_info == info_xrgb8888)
+			continue;
+
+		wl_display_add_shm_format(ec->wl_display, pixel_info->format);
+	}
 
 	wl_signal_init(&renderer->destroy_signal);