util: promote u_memory to src/util
[mesa.git] / src / gallium / auxiliary / util / u_format.c
index c50c807eb8997d589f55d2f5602e74555c318ccf..e43a619313e08b57ca34de5f9c84f9c6cf357e8c 100644 (file)
  * @author Jose Fonseca <jfonseca@vmware.com>
  */
 
-#include "u_math.h"
-#include "u_memory.h"
-#include "u_rect.h"
+#include "util/u_memory.h"
 #include "u_format.h"
+#include "u_format_s3tc.h"
+#include "u_surface.h"
+#include "util/u_math.h"
+
+#include "pipe/p_defines.h"
+
+
+boolean
+util_format_is_float(enum pipe_format format)
+{
+   const struct util_format_description *desc = util_format_description(format);
+   int i;
+
+   assert(desc);
+   if (!desc) {
+      return FALSE;
+   }
+
+   i = util_format_get_first_non_void_channel(format);
+   if (i < 0) {
+      return FALSE;
+   }
+
+   return desc->channel[i].type == UTIL_FORMAT_TYPE_FLOAT ? TRUE : FALSE;
+}
+
+
+/** Test if the format contains RGB, but not alpha */
+boolean
+util_format_has_alpha(enum pipe_format format)
+{
+   const struct util_format_description *desc =
+      util_format_description(format);
+
+   return (desc->colorspace == UTIL_FORMAT_COLORSPACE_RGB ||
+           desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) &&
+          desc->swizzle[3] != PIPE_SWIZZLE_1;
+}
+
+
+boolean
+util_format_is_luminance(enum pipe_format format)
+{
+   const struct util_format_description *desc =
+      util_format_description(format);
+
+   if ((desc->colorspace == UTIL_FORMAT_COLORSPACE_RGB ||
+        desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) &&
+       desc->swizzle[0] == PIPE_SWIZZLE_X &&
+       desc->swizzle[1] == PIPE_SWIZZLE_X &&
+       desc->swizzle[2] == PIPE_SWIZZLE_X &&
+       desc->swizzle[3] == PIPE_SWIZZLE_1) {
+      return TRUE;
+   }
+   return FALSE;
+}
+
+boolean
+util_format_is_alpha(enum pipe_format format)
+{
+   const struct util_format_description *desc =
+      util_format_description(format);
+
+   if ((desc->colorspace == UTIL_FORMAT_COLORSPACE_RGB ||
+        desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) &&
+       desc->swizzle[0] == PIPE_SWIZZLE_0 &&
+       desc->swizzle[1] == PIPE_SWIZZLE_0 &&
+       desc->swizzle[2] == PIPE_SWIZZLE_0 &&
+       desc->swizzle[3] == PIPE_SWIZZLE_X) {
+      return TRUE;
+   }
+   return FALSE;
+}
+
+boolean
+util_format_is_pure_integer(enum pipe_format format)
+{
+   const struct util_format_description *desc = util_format_description(format);
+   int i;
+
+   /* Find the first non-void channel. */
+   i = util_format_get_first_non_void_channel(format);
+   if (i == -1)
+      return FALSE;
+
+   return desc->channel[i].pure_integer ? TRUE : FALSE;
+}
+
+boolean
+util_format_is_pure_sint(enum pipe_format format)
+{
+   const struct util_format_description *desc = util_format_description(format);
+   int i;
+
+   i = util_format_get_first_non_void_channel(format);
+   if (i == -1)
+      return FALSE;
+
+   return (desc->channel[i].type == UTIL_FORMAT_TYPE_SIGNED && desc->channel[i].pure_integer) ? TRUE : FALSE;
+}
+
+boolean
+util_format_is_pure_uint(enum pipe_format format)
+{
+   const struct util_format_description *desc = util_format_description(format);
+   int i;
+
+   i = util_format_get_first_non_void_channel(format);
+   if (i == -1)
+      return FALSE;
+
+   return (desc->channel[i].type == UTIL_FORMAT_TYPE_UNSIGNED && desc->channel[i].pure_integer) ? TRUE : FALSE;
+}
+
+/**
+ * Returns true if all non-void channels are normalized signed.
+ */
+boolean
+util_format_is_snorm(enum pipe_format format)
+{
+   const struct util_format_description *desc = util_format_description(format);
+   int i;
+
+   if (desc->is_mixed)
+      return FALSE;
+
+   i = util_format_get_first_non_void_channel(format);
+   if (i == -1)
+      return FALSE;
+
+   return desc->channel[i].type == UTIL_FORMAT_TYPE_SIGNED &&
+          !desc->channel[i].pure_integer &&
+          desc->channel[i].normalized;
+}
+
+boolean
+util_format_is_snorm8(enum pipe_format format)
+{
+   const struct util_format_description *desc = util_format_description(format);
+   int i;
+
+   if (desc->is_mixed)
+      return FALSE;
+
+   i = util_format_get_first_non_void_channel(format);
+   if (i == -1)
+      return FALSE;
+
+   return desc->channel[i].type == UTIL_FORMAT_TYPE_SIGNED &&
+          !desc->channel[i].pure_integer &&
+          desc->channel[i].normalized &&
+          desc->channel[i].size == 8;
+}
+
+boolean
+util_format_is_luminance_alpha(enum pipe_format format)
+{
+   const struct util_format_description *desc =
+      util_format_description(format);
+
+   if ((desc->colorspace == UTIL_FORMAT_COLORSPACE_RGB ||
+        desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) &&
+       desc->swizzle[0] == PIPE_SWIZZLE_X &&
+       desc->swizzle[1] == PIPE_SWIZZLE_X &&
+       desc->swizzle[2] == PIPE_SWIZZLE_X &&
+       desc->swizzle[3] == PIPE_SWIZZLE_Y) {
+      return TRUE;
+   }
+   return FALSE;
+}
+
+
+boolean
+util_format_is_intensity(enum pipe_format format)
+{
+   const struct util_format_description *desc =
+      util_format_description(format);
+
+   if ((desc->colorspace == UTIL_FORMAT_COLORSPACE_RGB ||
+        desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) &&
+       desc->swizzle[0] == PIPE_SWIZZLE_X &&
+       desc->swizzle[1] == PIPE_SWIZZLE_X &&
+       desc->swizzle[2] == PIPE_SWIZZLE_X &&
+       desc->swizzle[3] == PIPE_SWIZZLE_X) {
+      return TRUE;
+   }
+   return FALSE;
+}
+
+boolean
+util_format_is_subsampled_422(enum pipe_format format)
+{
+   const struct util_format_description *desc =
+      util_format_description(format);
+
+   return desc->layout == UTIL_FORMAT_LAYOUT_SUBSAMPLED &&
+      desc->block.width == 2 &&
+      desc->block.height == 1 &&
+      desc->block.bits == 32;
+}
+
+/**
+ * Calculates the MRD for the depth format. MRD is used in depth bias
+ * for UNORM and unbound depth buffers. When the depth buffer is floating
+ * point, the depth bias calculation does not use the MRD. However, the
+ * default MRD will be 1.0 / ((1 << 24) - 1).
+ */
+double
+util_get_depth_format_mrd(const struct util_format_description *desc)
+{
+   /*
+    * Depth buffer formats without a depth component OR scenarios
+    * without a bound depth buffer default to D24.
+    */
+   double mrd = 1.0 / ((1 << 24) - 1);
+   unsigned depth_channel;
+
+   assert(desc);
+
+   /*
+    * Some depth formats do not store the depth component in the first
+    * channel, detect the format and adjust the depth channel. Get the
+    * swizzled depth component channel.
+    */
+   depth_channel = desc->swizzle[0];
+
+   if (desc->channel[depth_channel].type == UTIL_FORMAT_TYPE_UNSIGNED &&
+       desc->channel[depth_channel].normalized) {
+      int depth_bits;
+
+      depth_bits = desc->channel[depth_channel].size;
+      mrd = 1.0 / ((1ULL << depth_bits) - 1);
+   }
+
+   return mrd;
+}
 
 
 void
@@ -119,21 +353,198 @@ util_format_write_4ub(enum pipe_format format, const uint8_t *src, unsigned src_
    format_desc->pack_rgba_8unorm(dst_row, dst_stride, src_row, src_stride, w, h);
 }
 
+void
+util_format_read_4ui(enum pipe_format format,
+                     unsigned *dst, unsigned dst_stride,
+                     const void *src, unsigned src_stride,
+                     unsigned x, unsigned y, unsigned w, unsigned h)
+{
+   const struct util_format_description *format_desc;
+   const uint8_t *src_row;
+   uint32_t *dst_row;
+
+   format_desc = util_format_description(format);
+
+   assert(x % format_desc->block.width == 0);
+   assert(y % format_desc->block.height == 0);
+
+   src_row = (const uint8_t *)src + y*src_stride + x*(format_desc->block.bits/8);
+   dst_row = dst;
+
+   format_desc->unpack_rgba_uint(dst_row, dst_stride, src_row, src_stride, w, h);
+}
+
+void
+util_format_write_4ui(enum pipe_format format,
+                      const unsigned int *src, unsigned src_stride,
+                      void *dst, unsigned dst_stride,
+                      unsigned x, unsigned y, unsigned w, unsigned h)
+{
+   const struct util_format_description *format_desc;
+   uint8_t *dst_row;
+   const uint32_t *src_row;
+
+   format_desc = util_format_description(format);
+
+   assert(x % format_desc->block.width == 0);
+   assert(y % format_desc->block.height == 0);
+
+   dst_row = (uint8_t *)dst + y*dst_stride + x*(format_desc->block.bits/8);
+   src_row = src;
+
+   format_desc->pack_rgba_uint(dst_row, dst_stride, src_row, src_stride, w, h);
+}
+
+void
+util_format_read_4i(enum pipe_format format,
+                    int *dst, unsigned dst_stride,
+                    const void *src, unsigned src_stride,
+                    unsigned x, unsigned y, unsigned w, unsigned h)
+{
+   const struct util_format_description *format_desc;
+   const uint8_t *src_row;
+   int32_t *dst_row;
+
+   format_desc = util_format_description(format);
+
+   assert(x % format_desc->block.width == 0);
+   assert(y % format_desc->block.height == 0);
+
+   src_row = (const uint8_t *)src + y*src_stride + x*(format_desc->block.bits/8);
+   dst_row = dst;
+
+   format_desc->unpack_rgba_sint(dst_row, dst_stride, src_row, src_stride, w, h);
+}
+
+void
+util_format_write_4i(enum pipe_format format,
+                      const int *src, unsigned src_stride,
+                      void *dst, unsigned dst_stride,
+                      unsigned x, unsigned y, unsigned w, unsigned h)
+{
+   const struct util_format_description *format_desc;
+   uint8_t *dst_row;
+   const int32_t *src_row;
+
+   format_desc = util_format_description(format);
+
+   assert(x % format_desc->block.width == 0);
+   assert(y % format_desc->block.height == 0);
+
+   dst_row = (uint8_t *)dst + y*dst_stride + x*(format_desc->block.bits/8);
+   src_row = src;
+
+   format_desc->pack_rgba_sint(dst_row, dst_stride, src_row, src_stride, w, h);
+}
+
+/**
+ * Check if we can safely memcopy from the source format to the dest format.
+ * This basically covers the cases of a "used" channel copied to a typeless
+ * channel, plus some 1-channel cases.
+ * Examples of compatible copy formats include:
+ *    b8g8r8a8_unorm -> b8g8r8x8_unorm
+ *    a8r8g8b8_unorm -> x8r8g8b8_unorm
+ *    b5g5r5a1_unorm -> b5g5r5x1_unorm
+ *    b4g4r4a4_unorm -> b4g4r4x4_unorm
+ *    l8_unorm -> r8_unorm
+ *    i8_unorm -> l8_unorm
+ *    i8_unorm -> a8_unorm
+ *    i8_unorm -> r8_unorm
+ *    l16_unorm -> r16_unorm
+ *    z24_unorm_s8_uint -> z24x8_unorm
+ *    s8_uint_z24_unorm -> x8z24_unorm
+ *    r8g8b8a8_unorm -> r8g8b8x8_unorm
+ *    a8b8g8r8_srgb -> x8b8g8r8_srgb
+ *    b8g8r8a8_srgb -> b8g8r8x8_srgb
+ *    a8r8g8b8_srgb -> x8r8g8b8_srgb
+ *    a8b8g8r8_unorm -> x8b8g8r8_unorm
+ *    r10g10b10a2_uscaled -> r10g10b10x2_uscaled
+ *    r10sg10sb10sa2u_norm -> r10g10b10x2_snorm
+ */
+boolean
+util_is_format_compatible(const struct util_format_description *src_desc,
+                          const struct util_format_description *dst_desc)
+{
+   unsigned chan;
+
+   if (src_desc->format == dst_desc->format) {
+      return TRUE;
+   }
+
+   if (src_desc->layout != UTIL_FORMAT_LAYOUT_PLAIN ||
+       dst_desc->layout != UTIL_FORMAT_LAYOUT_PLAIN) {
+      return FALSE;
+   }
+
+   if (src_desc->block.bits != dst_desc->block.bits ||
+       src_desc->nr_channels != dst_desc->nr_channels ||
+       src_desc->colorspace != dst_desc->colorspace) {
+      return FALSE;
+   }
+
+   for (chan = 0; chan < 4; ++chan) {
+      if (src_desc->channel[chan].size !=
+          dst_desc->channel[chan].size) {
+         return FALSE;
+      }
+   }
+
+   for (chan = 0; chan < 4; ++chan) {
+      enum pipe_swizzle swizzle = dst_desc->swizzle[chan];
+
+      if (swizzle < 4) {
+         if (src_desc->swizzle[chan] != swizzle) {
+            return FALSE;
+         }
+         if ((src_desc->channel[swizzle].type !=
+              dst_desc->channel[swizzle].type) ||
+             (src_desc->channel[swizzle].normalized !=
+              dst_desc->channel[swizzle].normalized)) {
+            return FALSE;
+         }
+      }
+   }
+
+   return TRUE;
+}
+
 
-static INLINE boolean
+boolean
 util_format_fits_8unorm(const struct util_format_description *format_desc)
 {
    unsigned chan;
 
+   /*
+    * After linearized sRGB values require more than 8bits.
+    */
+
+   if (format_desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) {
+      return FALSE;
+   }
+
    switch (format_desc->layout) {
 
    case UTIL_FORMAT_LAYOUT_S3TC:
-   case UTIL_FORMAT_LAYOUT_RGTC:
       /*
        * These are straight forward.
        */
-
       return TRUE;
+   case UTIL_FORMAT_LAYOUT_RGTC:
+      if (format_desc->format == PIPE_FORMAT_RGTC1_SNORM ||
+          format_desc->format == PIPE_FORMAT_RGTC2_SNORM ||
+          format_desc->format == PIPE_FORMAT_LATC1_SNORM ||
+          format_desc->format == PIPE_FORMAT_LATC2_SNORM)
+         return FALSE;
+      return TRUE;
+   case UTIL_FORMAT_LAYOUT_BPTC:
+      if (format_desc->format == PIPE_FORMAT_BPTC_RGBA_UNORM)
+         return TRUE;
+      return FALSE;
+
+   case UTIL_FORMAT_LAYOUT_ETC:
+      if (format_desc->format == PIPE_FORMAT_ETC1_RGB8)
+         return TRUE;
+      return FALSE;
 
    case UTIL_FORMAT_LAYOUT_PLAIN:
       /*
@@ -176,7 +587,7 @@ util_format_fits_8unorm(const struct util_format_description *format_desc)
 }
 
 
-void
+boolean
 util_format_translate(enum pipe_format dst_format,
                       void *dst, unsigned dst_stride,
                       unsigned dst_x, unsigned dst_y,
@@ -189,11 +600,14 @@ util_format_translate(enum pipe_format dst_format,
    const struct util_format_description *src_format_desc;
    uint8_t *dst_row;
    const uint8_t *src_row;
-   unsigned y_step;
+   unsigned x_step, y_step;
    unsigned dst_step;
    unsigned src_step;
 
-   if (dst_format == src_format) {
+   dst_format_desc = util_format_description(dst_format);
+   src_format_desc = util_format_description(src_format);
+
+   if (util_is_format_compatible(src_format_desc, dst_format_desc)) {
       /*
        * Trivial case.
        */
@@ -201,12 +615,9 @@ util_format_translate(enum pipe_format dst_format,
       util_copy_rect(dst, dst_format, dst_stride,  dst_x, dst_y,
                      width, height, src, (int)src_stride,
                      src_x, src_y);
-      return;
+      return TRUE;
    }
 
-   dst_format_desc = util_format_description(dst_format);
-   src_format_desc = util_format_description(src_format);
-
    assert(dst_x % dst_format_desc->block.width == 0);
    assert(dst_y % dst_format_desc->block.height == 0);
    assert(src_x % src_format_desc->block.width == 0);
@@ -221,6 +632,7 @@ util_format_translate(enum pipe_format dst_format,
     */
 
    y_step = MAX2(dst_format_desc->block.height, src_format_desc->block.height);
+   x_step = MAX2(dst_format_desc->block.width, src_format_desc->block.width);
    assert(y_step % dst_format_desc->block.height == 0);
    assert(y_step % src_format_desc->block.height == 0);
 
@@ -232,15 +644,60 @@ util_format_translate(enum pipe_format dst_format,
     * TODO: Add a special case for formats that are mere swizzles of each other
     */
 
+   if (src_format_desc->colorspace == UTIL_FORMAT_COLORSPACE_ZS ||
+       dst_format_desc->colorspace == UTIL_FORMAT_COLORSPACE_ZS) {
+      float *tmp_z = NULL;
+      uint8_t *tmp_s = NULL;
+
+      assert(x_step == 1);
+      assert(y_step == 1);
+
+      if (src_format_desc->unpack_z_float &&
+          dst_format_desc->pack_z_float) {
+         tmp_z = MALLOC(width * sizeof *tmp_z);
+      }
+
+      if (src_format_desc->unpack_s_8uint &&
+          dst_format_desc->pack_s_8uint) {
+         tmp_s = MALLOC(width * sizeof *tmp_s);
+      }
+
+      while (height--) {
+         if (tmp_z) {
+            src_format_desc->unpack_z_float(tmp_z, 0, src_row, src_stride, width, 1);
+            dst_format_desc->pack_z_float(dst_row, dst_stride, tmp_z, 0, width, 1);
+         }
+
+         if (tmp_s) {
+            src_format_desc->unpack_s_8uint(tmp_s, 0, src_row, src_stride, width, 1);
+            dst_format_desc->pack_s_8uint(dst_row, dst_stride, tmp_s, 0, width, 1);
+         }
+
+         dst_row += dst_step;
+         src_row += src_step;
+      }
+
+      FREE(tmp_s);
+
+      FREE(tmp_z);
+
+      return TRUE;
+   }
+
    if (util_format_fits_8unorm(src_format_desc) ||
        util_format_fits_8unorm(dst_format_desc)) {
       unsigned tmp_stride;
       uint8_t *tmp_row;
 
-      tmp_stride = width * 4 * sizeof *tmp_row;
+      if (!src_format_desc->unpack_rgba_8unorm ||
+          !dst_format_desc->pack_rgba_8unorm) {
+         return FALSE;
+      }
+
+      tmp_stride = MAX2(width, x_step) * 4 * sizeof *tmp_row;
       tmp_row = MALLOC(y_step * tmp_stride);
       if (!tmp_row)
-         return;
+         return FALSE;
 
       while (height >= y_step) {
          src_format_desc->unpack_rgba_8unorm(tmp_row, tmp_stride, src_row, src_stride, width, y_step);
@@ -262,10 +719,15 @@ util_format_translate(enum pipe_format dst_format,
       unsigned tmp_stride;
       float *tmp_row;
 
-      tmp_stride = width * 4 * sizeof *tmp_row;
+      if (!src_format_desc->unpack_rgba_float ||
+          !dst_format_desc->pack_rgba_float) {
+         return FALSE;
+      }
+
+      tmp_stride = MAX2(width, x_step) * 4 * sizeof *tmp_row;
       tmp_row = MALLOC(y_step * tmp_stride);
       if (!tmp_row)
-         return;
+         return FALSE;
 
       while (height >= y_step) {
          src_format_desc->unpack_rgba_float(tmp_row, tmp_stride, src_row, src_stride, width, y_step);
@@ -283,4 +745,123 @@ util_format_translate(enum pipe_format dst_format,
 
       FREE(tmp_row);
    }
+   return TRUE;
+}
+
+boolean
+util_format_translate_3d(enum pipe_format dst_format,
+                         void *dst, unsigned dst_stride,
+                         unsigned dst_slice_stride,
+                         unsigned dst_x, unsigned dst_y,
+                         unsigned dst_z,
+                         enum pipe_format src_format,
+                         const void *src, unsigned src_stride,
+                         unsigned src_slice_stride,
+                         unsigned src_x, unsigned src_y,
+                         unsigned src_z, unsigned width,
+                         unsigned height, unsigned depth)
+{
+   uint8_t *dst_layer;
+   const uint8_t *src_layer;
+   unsigned z;
+   dst_layer = dst;
+   src_layer = src;
+   dst_layer += dst_z * dst_slice_stride;
+   src_layer += src_z * src_slice_stride;
+   for (z = 0; z < depth; ++z) {
+      if (!util_format_translate(dst_format, dst_layer, dst_stride,
+                                 dst_x, dst_y,
+                                 src_format, src_layer, src_stride,
+                                 src_x, src_y,
+                                 width, height))
+          return FALSE;
+
+      dst_layer += dst_slice_stride;
+      src_layer += src_slice_stride;
+   }
+   return TRUE;
+}
+
+void util_format_compose_swizzles(const unsigned char swz1[4],
+                                  const unsigned char swz2[4],
+                                  unsigned char dst[4])
+{
+   unsigned i;
+
+   for (i = 0; i < 4; i++) {
+      dst[i] = swz2[i] <= PIPE_SWIZZLE_W ?
+               swz1[swz2[i]] : swz2[i];
+   }
+}
+
+void util_format_apply_color_swizzle(union pipe_color_union *dst,
+                                     const union pipe_color_union *src,
+                                     const unsigned char swz[4],
+                                     const boolean is_integer)
+{
+   unsigned c;
+
+   if (is_integer) {
+      for (c = 0; c < 4; ++c) {
+         switch (swz[c]) {
+         case PIPE_SWIZZLE_X:   dst->ui[c] = src->ui[0]; break;
+         case PIPE_SWIZZLE_Y: dst->ui[c] = src->ui[1]; break;
+         case PIPE_SWIZZLE_Z:  dst->ui[c] = src->ui[2]; break;
+         case PIPE_SWIZZLE_W: dst->ui[c] = src->ui[3]; break;
+         default:
+            dst->ui[c] = (swz[c] == PIPE_SWIZZLE_1) ? 1 : 0;
+            break;
+         }
+      }
+   } else {
+      for (c = 0; c < 4; ++c) {
+         switch (swz[c]) {
+         case PIPE_SWIZZLE_X:   dst->f[c] = src->f[0]; break;
+         case PIPE_SWIZZLE_Y: dst->f[c] = src->f[1]; break;
+         case PIPE_SWIZZLE_Z:  dst->f[c] = src->f[2]; break;
+         case PIPE_SWIZZLE_W: dst->f[c] = src->f[3]; break;
+         default:
+            dst->f[c] = (swz[c] == PIPE_SWIZZLE_1) ? 1.0f : 0.0f;
+            break;
+         }
+      }
+   }
+}
+
+void pipe_swizzle_4f(float *dst, const float *src,
+                            const unsigned char swz[4])
+{
+   unsigned i;
+
+   for (i = 0; i < 4; i++) {
+      if (swz[i] <= PIPE_SWIZZLE_W)
+         dst[i] = src[swz[i]];
+      else if (swz[i] == PIPE_SWIZZLE_0)
+         dst[i] = 0;
+      else if (swz[i] == PIPE_SWIZZLE_1)
+         dst[i] = 1;
+   }
+}
+
+void util_format_unswizzle_4f(float *dst, const float *src,
+                              const unsigned char swz[4])
+{
+   unsigned i;
+
+   for (i = 0; i < 4; i++) {
+      switch (swz[i]) {
+      case PIPE_SWIZZLE_X:
+         dst[0] = src[i];
+         break;
+      case PIPE_SWIZZLE_Y:
+         dst[1] = src[i];
+         break;
+      case PIPE_SWIZZLE_Z:
+         dst[2] = src[i];
+         break;
+      case PIPE_SWIZZLE_W:
+         dst[3] = src[i];
+         break;
+      }
+   }
 }