<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Dec 9, 2014 at 4:07 AM, Iago Toral Quiroga <span dir="ltr"><<a href="mailto:itoral@igalia.com" target="_blank">itoral@igalia.com</a>></span> wrote:<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">From: Jason Ekstrand <<a href="mailto:jason.ekstrand@intel.com">jason.ekstrand@intel.com</a>><br>
<br>
v2 by Iago Toral <<a href="mailto:itoral@igalia.com">itoral@igalia.com</a>>:<br>
<br>
- When testing if we can directly pack we should use the src format to check<br>
  if we are packing from an RGBA format. The original code used the dst format<br>
  for the ubyte case by mistake.<br>
- Fixed incorrect number of bits for dst, it was computed using the src format<br>
  instead of the dst format.<br>
- If the dst format is an array format, check if it is signed. We were only<br>
  checking this for the case where it was not an array format, but we need<br>
  to know this in both scenarios.<br>
- Fixed incorrect swizzle transform for the cases where we convert between<br>
  array formats.<br>
- Compute is_signed and bits only once and for the dst format. We were<br>
  computing these for the src format too but they were overwritten by the<br>
  dst values immediately after.<br>
- Be more careful when selecting the integer path. Specifically, check that<br>
  both src and dst are integer types. Checking only one of them should suffice<br>
  since OpenGL does not allow conversions between normalized and integer types,<br>
  but putting extra care here makes sense and also makes the actual requirements<br>
  for this path more clear.<br>
- The format argument for pack functions is the destination format we are<br>
  packing to, not the source format (which has to be RGBA).<br>
- Expose RGBA8888_* to other files. These will come in handy when in need to<br>
  test if a given array format is RGBA or in need to pass RGBA formats to<br>
  mesa_format_convert.<br>
<br>
v3 by Samuel Iglesias <<a href="mailto:siglesias@igalia.com">siglesias@igalia.com</a>>:<br>
<br>
- Add an RGBA8888_INT definition.<br>
<br>
v4 by Iago Toral <<a href="mailto:itoral@igalia.com">itoral@igalia.com</a>> after review by Jason Ekstrand:<br>
<br>
- Added documentation for _mesa_format_convert.<br>
- Added additional explanatory comments for integer conversions.<br>
- Ensure that we use _messa_swizzle_and_convert for all signed source formats.<br>
- Squashed: do not directly (un)pack to RGBA UINT if the source is not unsigned.<br>
<br>
v5 by Iago Toral <<a href="mailto:itoral@igalia.com">itoral@igalia.com</a>>:<br>
<br>
- Adapted to the new implementation of mesa_array_format as a plain uint32_t<br>
  bitfield.<br>
---<br>
 src/mesa/main/format_utils.c | 408 +++++++++++++++++++++++++++++++++++++++++++<br>
 src/mesa/main/format_utils.h |  10 ++<br>
 2 files changed, 418 insertions(+)<br>
<br>
diff --git a/src/mesa/main/format_utils.c b/src/mesa/main/format_utils.c<br>
index 201231e..541d19a 100644<br>
--- a/src/mesa/main/format_utils.c<br>
+++ b/src/mesa/main/format_utils.c<br>
@@ -24,6 +24,414 @@<br>
<br>
 #include "format_utils.h"<br>
 #include "glformats.h"<br>
+#include "format_pack.h"<br>
+#include "format_unpack.h"<br>
+<br>
+mesa_array_format RGBA8888_FLOAT =<br>
+   MESA_ARRAY_FORMAT(4, 1, 1, 1, 4, 0, 1, 2, 3);<br>
+<br>
+mesa_array_format RGBA8888_UBYTE =<br>
+   MESA_ARRAY_FORMAT(1, 0, 0, 1, 4, 0, 1, 2, 3);<br>
+<br>
+mesa_array_format RGBA8888_UINT =<br>
+   MESA_ARRAY_FORMAT(4, 0, 0, 0, 4, 0, 1, 2, 3);<br>
+<br>
+mesa_array_format RGBA8888_INT =<br>
+   MESA_ARRAY_FORMAT(4, 1, 0, 0, 4, 0, 1, 2, 3);<br>
+<br>
+static void<br>
+invert_swizzle(uint8_t dst[4], const uint8_t src[4])<br>
+{<br>
+   int i, j;<br>
+<br>
+   dst[0] = MESA_FORMAT_SWIZZLE_NONE;<br>
+   dst[1] = MESA_FORMAT_SWIZZLE_NONE;<br>
+   dst[2] = MESA_FORMAT_SWIZZLE_NONE;<br>
+   dst[3] = MESA_FORMAT_SWIZZLE_NONE;<br>
+<br>
+   for (i = 0; i < 4; ++i)<br>
+      for (j = 0; j < 4; ++j)<br>
+         if (src[j] == i && dst[i] == MESA_FORMAT_SWIZZLE_NONE)<br>
+            dst[i] = j;<br>
+}<br>
+<br>
+static GLenum<br>
+gl_type_for_array_format_datatype(enum mesa_array_format_datatype type)<br>
+{<br>
+   switch (type) {<br>
+   case MESA_ARRAY_FORMAT_TYPE_UBYTE:<br>
+      return GL_UNSIGNED_BYTE;<br>
+   case MESA_ARRAY_FORMAT_TYPE_USHORT:<br>
+      return GL_UNSIGNED_SHORT;<br>
+   case MESA_ARRAY_FORMAT_TYPE_UINT:<br>
+      return GL_UNSIGNED_INT;<br>
+   case MESA_ARRAY_FORMAT_TYPE_BYTE:<br>
+      return GL_BYTE;<br>
+   case MESA_ARRAY_FORMAT_TYPE_SHORT:<br>
+      return GL_SHORT;<br>
+   case MESA_ARRAY_FORMAT_TYPE_INT:<br>
+      return GL_INT;<br>
+   case MESA_ARRAY_FORMAT_TYPE_HALF:<br>
+      return GL_HALF_FLOAT;<br>
+   case MESA_ARRAY_FORMAT_TYPE_FLOAT:<br>
+      return GL_FLOAT;<br>
+   default:<br>
+      assert(!"Invalid datatype");<br>
+      return GL_NONE;<br>
+   }<br>
+}<br>
+<br>
+/**<br>
+ * This can be used to convert between most color formats.<br>
+ *<br>
+ * Limitations:<br>
+ * - This function doesn't handle GL_COLOR_INDEX or YCBCR formats.<br>
+ * - This function doesn't handle byte-swapping or transferOps, these should<br>
+ *   be handled by the caller.<br>
+ *<br>
+ * \param void_dst  The address where converted color data will be stored.<br>
+ *                  The caller must ensure that the buffer is large enough<br>
+ *                  to hold the converted pixel data.<br>
+ * \param dst_format  The destination color format. It can be a mesa_format<br>
+ *                    or a mesa_array_format represented as an uint32_t.<br>
+ * \param dst_stride  The stride of the destination format in bytes.<br>
+ * \param void_src  The address of the source color data to convert.<br>
+ * \param src_format  The source color format. It can be a mesa_format<br>
+ *                    or a mesa_array_format represented as an uint32_t.<br>
+ * \param src_stride  The stride of the source format in bytes.<br>
+ * \param width  The width, in pixels, of the source image to convert.<br>
+ * \param height  The height, in pixels, of the source image to convert.<br>
+ */<br>
+void<br>
+_mesa_format_convert(void *void_dst, uint32_t dst_format, size_t dst_stride,<br>
+                     void *void_src, uint32_t src_format, size_t src_stride,<br>
+                     size_t width, size_t height)<br>
+{<br>
+   uint8_t *dst = (uint8_t *)void_dst;<br>
+   uint8_t *src = (uint8_t *)void_src;<br>
+   mesa_array_format src_array_format, dst_array_format;<br>
+   bool src_format_is_mesa_array_format, dst_format_is_mesa_array_format;<br>
+   uint8_t src2dst[4], src2rgba[4], rgba2dst[4], dst2rgba[4];<br>
+   GLenum src_gl_type, dst_gl_type, common_gl_type;<br>
+   bool normalized, dst_integer, src_integer, is_signed;<br>
+   int src_num_channels = 0, dst_num_channels = 0;<br>
+   uint8_t (*tmp_ubyte)[4];<br>
+   float (*tmp_float)[4];<br>
+   uint32_t (*tmp_uint)[4];<br>
+   int i, bits;<br>
+   size_t row;<br>
+<br>
+   if (_mesa_format_is_mesa_array_format(src_format)) {<br>
+      src_format_is_mesa_array_format = true;<br>
+      src_array_format = src_format;<br>
+   } else {<br>
+      assert(_mesa_is_format_color_format(src_format));<br>
+      src_format_is_mesa_array_format = false;<br>
+      src_array_format = _mesa_format_to_array_format(src_format);<br>
+   }<br>
+<br>
+   if (_mesa_format_is_mesa_array_format(dst_format)) {<br>
+      dst_format_is_mesa_array_format = true;<br>
+      dst_array_format = dst_format;<br>
+   } else {<br>
+      assert(_mesa_is_format_color_format(dst_format));<br>
+      dst_format_is_mesa_array_format = false;<br>
+      dst_array_format = _mesa_format_to_array_format(dst_format);<br>
+   }<br>
+<br>
+   /* Handle the cases where we can directly unpack */<br>
+   if (!src_format_is_mesa_array_format) {<br>
+      if (dst_array_format == RGBA8888_FLOAT) {<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_unpack_rgba_row(src_format, width,<br>
+                                  src, (float (*)[4])dst);<br>
+            src += src_stride;<br>
+            dst += dst_stride;<br>
+         }<br>
+         return;<br>
+      } else if (dst_array_format == RGBA8888_UBYTE) {<br>
+         assert(!_mesa_is_format_integer_color(src_format));<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_unpack_ubyte_rgba_row(src_format, width,<br>
+                                        src, (uint8_t (*)[4])dst);<br>
+            src += src_stride;<br>
+            dst += dst_stride;<br>
+         }<br>
+         return;<br>
+      } else if (dst_array_format == RGBA8888_UINT &&<br>
+                 _mesa_is_format_unsigned(src_format)) {<br>
+         assert(_mesa_is_format_integer_color(src_format));<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_unpack_uint_rgba_row(src_format, width,<br>
+                                       src, (uint32_t (*)[4])dst);<br>
+            src += src_stride;<br>
+            dst += dst_stride;<br>
+         }<br>
+         return;<br>
+      }<br>
+   }<br>
+<br>
+   /* Handle the cases where we can directly pack */<br>
+   if (!dst_format_is_mesa_array_format) {<br>
+      if (src_array_format == RGBA8888_FLOAT) {<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_pack_float_rgba_row(dst_format, width,<br>
+                                      (const float (*)[4])src, dst);<br>
+            src += src_stride;<br>
+            dst += dst_stride;<br>
+         }<br>
+         return;<br>
+      } else if (src_array_format == RGBA8888_UBYTE) {<br>
+         assert(!_mesa_is_format_integer_color(dst_format));<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_pack_ubyte_rgba_row(dst_format, width,<br>
+                                      (const uint8_t (*)[4])src, dst);<br>
+            src += src_stride;<br>
+            dst += dst_stride;<br>
+         }<br>
+         return;<br>
+      } else if (src_array_format == RGBA8888_UINT &&<br>
+                 _mesa_is_format_unsigned(dst_format)) {<br>
+         assert(_mesa_is_format_integer_color(dst_format));<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_pack_uint_rgba_row(dst_format, width,<br>
+                                     (const uint32_t (*)[4])src, dst);<br>
+            src += src_stride;<br>
+            dst += dst_stride;<br>
+         }<br>
+         return;<br>
+      }<br>
+   }<br>
+<br>
+   /* Handle conversions between array formats */<br>
+   normalized = false;<br>
+   if (src_array_format) {<br>
+      enum mesa_array_format_datatype datatype =<br>
+         _mesa_array_format_get_datatype(src_array_format);<br>
+      src_gl_type = gl_type_for_array_format_datatype(datatype);<br>
+<br>
+      src_num_channels = _mesa_array_format_get_num_channels(src_array_format);<br>
+<br>
+      _mesa_array_format_get_swizzle(src_array_format, src2rgba);<br>
+<br>
+      normalized = _mesa_array_format_is_normalized(src_array_format);<br>
+   }<br>
+<br>
+   if (dst_array_format) {<br>
+      enum mesa_array_format_datatype datatype =<br>
+         _mesa_array_format_get_datatype(dst_array_format);<br>
+      dst_gl_type = gl_type_for_array_format_datatype(datatype);<br>
+<br>
+      dst_num_channels = _mesa_array_format_get_num_channels(dst_array_format);<br>
+<br>
+      _mesa_array_format_get_swizzle(dst_array_format, dst2rgba);<br>
+      invert_swizzle(rgba2dst, dst2rgba);<br>
+<br>
+      normalized |= _mesa_array_format_is_normalized(dst_array_format);<br>
+   }<br>
+<br>
+   if (src_array_format && dst_array_format) {<br>
+      assert(_mesa_array_format_is_normalized(src_array_format) ==<br>
+             _mesa_array_format_is_normalized(dst_array_format));<br>
+<br>
+      for (i = 0; i < 4; i++) {<br>
+         if (rgba2dst[i] > MESA_FORMAT_SWIZZLE_W) {<br>
+            src2dst[i] = rgba2dst[i];<br>
+         } else {<br>
+            src2dst[i] = src2rgba[rgba2dst[i]];<br>
+         }<br>
+      }<br>
+<br>
+      for (row = 0; row < height; ++row) {<br>
+         _mesa_swizzle_and_convert(dst, dst_gl_type, dst_num_channels,<br>
+                                   src, src_gl_type, src_num_channels,<br>
+                                   src2dst, normalized, width);<br>
+         src += src_stride;<br>
+         dst += dst_stride;<br>
+      }<br>
+      return;<br>
+   }<br>
+<br>
+   /* At this point, we're fresh out of fast-paths and we need to convert<br>
+    * to float, uint32, or, if we're lucky, uint8.<br>
+    */<br>
+   dst_integer = false;<br>
+   src_integer = false;<br>
+<br>
+   if (src_array_format) {<br>
+      if (!_mesa_array_format_is_float(src_array_format) &&<br>
+          !_mesa_array_format_is_normalized(src_array_format))<br>
+         src_integer = true;<br>
+   } else {<br>
+      switch (_mesa_get_format_datatype(src_format)) {<br>
+      case GL_UNSIGNED_INT:<br>
+      case GL_INT:<br>
+         src_integer = true;<br>
+         break;<br>
+      }<br>
+   }<br>
+<br>
+   /* If the destination format is signed but the source is unsigned, then we<br>
+    * don't loose any data by converting to a signed intermediate format above<br>
+    * and beyond the precision that we loose in the conversion itself. If the<br>
+    * destination is unsigned then, by using an unsigned intermediate format,<br>
+    * we make the conversion function that converts from the source to the<br>
+    * intermediate format take care of truncating at zero. The exception here<br>
+    * is if the intermediate format is float, in which case the first<br>
+    * conversion will leave it signed and the second conversion will truncate<br>
+    * at zero.<br>
+    */<br>
+   is_signed = false;<br>
+   if (dst_array_format) {<br>
+      if (!_mesa_array_format_is_float(dst_array_format) &&<br>
+          !_mesa_array_format_is_normalized(dst_array_format))<br>
+         dst_integer = true;<br>
+      is_signed = _mesa_array_format_is_signed(dst_array_format);<br>
+      bits = 8 * _mesa_array_format_get_type_size(dst_array_format);<br>
+   } else {<br>
+      switch (_mesa_get_format_datatype(dst_format)) {<br>
+      case GL_UNSIGNED_NORMALIZED:<br>
+         is_signed = false;<br>
+         break;<br>
+      case GL_SIGNED_NORMALIZED:<br>
+         is_signed = true;<br>
+         break;<br>
+      case GL_FLOAT:<br>
+         is_signed = true;<br>
+         break;<br>
+      case GL_UNSIGNED_INT:<br>
+         is_signed = false;<br>
+         dst_integer = true;<br>
+         break;<br>
+      case GL_INT:<br>
+         is_signed = true;<br>
+         dst_integer = true;<br>
+         break;<br>
+      }<br>
+      bits = _mesa_get_format_max_bits(dst_format);<br>
+   }<br>
+<br>
+   assert(src_integer == dst_integer);<br>
+<br>
+   if (src_integer && dst_integer) {<br>
+      tmp_uint = malloc(width * height * sizeof(*tmp_uint));<br>
+<br>
+      /* The [un]packing functions for unsigned datatypes treat the 32-bit<br>
+       * integer array as signed for signed formats and as unsigned for<br>
+       * unsigned formats. This is a bit of a problem if we ever convert from<br>
+       * a signed to an unsigned format because the unsigned packing function<br>
+       * doesn't know that the input is signed and will treat it as unsigned<br>
+       * and not do the trunctation. The thing that saves us here is that all<br>
+       * of the packed formats are unsigned, so we can just always use<br>
+       * _mesa_swizzle_and_convert for signed formats, which is aware of the<br>
+       * truncation problem.<br>
+       */<br>
+      common_gl_type = is_signed ? GL_INT : GL_UNSIGNED_INT;<br>
+      if (src_array_format) {<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_swizzle_and_convert(tmp_uint + row * width, common_gl_type, 4,<br>
+                                      src, src_gl_type, src_num_channels,<br>
+                                      src2rgba, normalized, width);<br>
+            src += src_stride;<br>
+         }<br>
+      } else {<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_unpack_uint_rgba_row(src_format, width,<br>
+                                       src, tmp_uint + row * width);<br>
+            src += src_stride;<br>
+         }<br>
+      }<br>
+<br>
+      /* At this point, we have already done the truncation if the source is<br>
+       * signed but the destination is unsigned, so no need to force the<br>
+       * _mesa_swizzle_and_convert path.<br>
+       */<br>
+      if (dst_format_is_mesa_array_format) {<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_swizzle_and_convert(dst, dst_gl_type, dst_num_channels,<br>
+                                      tmp_uint + row * width, common_gl_type, 4,<br>
+                                      rgba2dst, normalized, width);<br>
+            dst += dst_stride;<br>
+         }<br>
+      } else {<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_pack_uint_rgba_row(dst_format, width,<br>
+                                     (const uint32_t (*)[4])tmp_uint + row * width, dst);<br>
+            dst += dst_stride;<br>
+         }<br>
+      }<br>
+<br>
+      free(tmp_uint);<br>
+   } else if (is_signed || bits > 8) {<br>
+      tmp_float = malloc(width * height * sizeof(*tmp_float));<br>
+<br>
+      if (src_format_is_mesa_array_format) {<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_swizzle_and_convert(tmp_float + row * width, GL_FLOAT, 4,<br>
+                                      src, src_gl_type, src_num_channels,<br>
+                                      src2rgba, normalized, width);<br>
+            src += src_stride;<br>
+         }<br>
+      } else {<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_unpack_rgba_row(src_format, width,<br>
+                                  src, tmp_float + row * width);<br>
+            src += src_stride;<br>
+         }<br>
+      }<br>
+<br>
+      if (dst_format_is_mesa_array_format) {<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_swizzle_and_convert(dst, dst_gl_type, dst_num_channels,<br>
+                                      tmp_float + row * width, GL_FLOAT, 4,<br>
+                                      rgba2dst, normalized, width);<br>
+            dst += dst_stride;<br>
+         }<br>
+      } else {<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_pack_float_rgba_row(dst_format, width,<br>
+                                      (const float (*)[4])tmp_float + row * width, dst);<br>
+            dst += dst_stride;<br>
+         }<br>
+      }<br>
+<br>
+      free(tmp_float);<br>
+   } else {<br>
+      tmp_ubyte = malloc(width * height * sizeof(*tmp_ubyte));<br>
+<br>
+      if (src_format_is_mesa_array_format) {<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_swizzle_and_convert(tmp_ubyte + row * width, GL_UNSIGNED_BYTE, 4,<br>
+                                      src, src_gl_type, src_num_channels,<br>
+                                      src2rgba, normalized, width);<br>
+            src += src_stride;<br>
+         }<br>
+      } else {<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_unpack_ubyte_rgba_row(src_format, width,<br>
+                                        src, tmp_ubyte + row * width);<br>
+            src += src_stride;<br>
+         }<br>
+      }<br>
+<br>
+      if (dst_format_is_mesa_array_format) {<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_swizzle_and_convert(dst, dst_gl_type, dst_num_channels,<br>
+                                      tmp_ubyte + row * width, GL_UNSIGNED_BYTE, 4,<br>
+                                      rgba2dst, normalized, width);<br>
+            dst += dst_stride;<br>
+         }<br>
+      } else {<br>
+         for (row = 0; row < height; ++row) {<br>
+            _mesa_pack_ubyte_rgba_row(dst_format, width,<br>
+                                      (const uint8_t (*)[4])tmp_ubyte + row * width, dst);<br>
+            dst += dst_stride;<br>
+         }<br>
+      }<br>
+<br>
+      free(tmp_ubyte);<br>
+   }<br>
+}<br>
<br>
 static const uint8_t map_identity[7] = { 0, 1, 2, 3, 4, 5, 6 };<br>
 static const uint8_t map_3210[7] = { 3, 2, 1, 0, 4, 5, 6 };<br>
diff --git a/src/mesa/main/format_utils.h b/src/mesa/main/format_utils.h<br>
index fb00f55..8d25e51 100644<br>
--- a/src/mesa/main/format_utils.h<br>
+++ b/src/mesa/main/format_utils.h<br>
@@ -34,6 +34,11 @@<br>
 #include "imports.h"<br>
 #include "macros.h"<br>
<br>
+extern mesa_array_format RGBA8888_FLOAT;<br>
+extern mesa_array_format RGBA8888_UBYTE;<br>
+extern mesa_array_format RGBA8888_UINT;<br>
+extern mesa_array_format RGBA8888_INT;<br></blockquote><div><br></div><div>Make these const.  Otherwise, looks good.<br><br></div><div>Reviewed-by: Jason Ekstrand <<a href="mailto:jason.ekstrand@intel.com">jason.ekstrand@intel.com</a>><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
 /* Only guaranteed to work for BITS <= 32 */<br>
 #define MAX_UINT(BITS) ((BITS) == 32 ? UINT32_MAX : ((1u << (BITS)) - 1))<br>
 #define MAX_INT(BITS) ((int)MAX_UINT((BITS) - 1))<br>
@@ -173,4 +178,9 @@ _mesa_swizzle_and_convert(void *dst, GLenum dst_type, int num_dst_channels,<br>
                           const void *src, GLenum src_type, int num_src_channels,<br>
                           const uint8_t swizzle[4], bool normalized, int count);<br>
<br>
+void<br>
+_mesa_format_convert(void *void_dst, uint32_t dst_format, size_t dst_stride,<br>
+                     void *void_src, uint32_t src_format, size_t src_stride,<br>
+                     size_t width, size_t height);<br>
+<br>
 #endif<br>
<span class="HOEnZb"><font color="#888888">--<br>
1.9.1<br>
<br>
_______________________________________________<br>
mesa-dev mailing list<br>
<a href="mailto:mesa-dev@lists.freedesktop.org">mesa-dev@lists.freedesktop.org</a><br>
<a href="http://lists.freedesktop.org/mailman/listinfo/mesa-dev" target="_blank">http://lists.freedesktop.org/mailman/listinfo/mesa-dev</a><br>
</font></span></blockquote></div></div></div>