content: /*
   * Copyright (C) 2013 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.renderscript;
  
  /**
   * <p>A Type describes the {@link android.renderscript.Element} and dimensions used for an {@link
   * android.renderscript.Allocation} or a parallel operation. Types are created through {@link
   * android.renderscript.Type.Builder}.</p>
   *
   * <p>A Type always includes an {@link android.renderscript.Element} and an X
   * dimension. A Type may be multidimensional, up to three dimensions. A nonzero
   * value in the Y or Z dimensions indicates that the dimension is present. Note
   * that a Type with only a given X dimension and a Type with the same X
   * dimension but Y = 1 are not equivalent.</p>
   *
   * <p>A Type also supports inclusion of level of detail (LOD) or cube map
   * faces. LOD and cube map faces are booleans to indicate present or not
   * present. </p>
   *
   * <p>A Type also supports YUV format information to support an
   * {@link android.renderscript.Allocation} in a YUV format. The YUV formats
   * supported are {@link android.graphics.ImageFormat#YV12},
   * {@link android.graphics.ImageFormat#NV21}, and
   * {@link android.graphics.ImageFormat#YUV_420_888}</p>
   *
   * <div class="special reference">
   * <h3>Developer Guides</h3>
   * <p>For more information about creating an application that uses RenderScript, read the
   * <a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a> developer guide.</p>
   * </div>
   **/
  public class Type extends BaseObj {
      int mDimX;
      int mDimY;
      int mDimZ;
      boolean mDimMipmaps;
      boolean mDimFaces;
      int mDimYuv;
      int mElementCount;
      Element mElement;
      int mArrays[];
  
      static final int mMaxArrays = 4;
  
      public enum CubemapFace {
          POSITIVE_X (0),
          NEGATIVE_X (1),
          POSITIVE_Y (2),
          NEGATIVE_Y (3),
          POSITIVE_Z (4),
          NEGATIVE_Z (5),
          @Deprecated
          POSITVE_X (0),
          @Deprecated
          POSITVE_Y (2),
          @Deprecated
          POSITVE_Z (4);
  
          int mID;
          CubemapFace(int id) {
              mID = id;
          }
      }
  
      /**
       * Return the element associated with this Type.
       *
       * @return Element
       */
      public Element getElement() {
          return mElement;
      }
  
      /**
       * Return the value of the X dimension.
       *
       * @return int
       */
      public int getX() {
          return mDimX;
      }
  
      /**
       * Return the value of the Y dimension or 0 for a 1D allocation.
       *
       * @return int
       */
      public int getY() {
          return mDimY;
      }
  
      /**
       * Return the value of the Z dimension or 0 for a 1D or 2D allocation.
       *
       * @return int
       */
      public int getZ() {
          return mDimZ;
      }
  
      /**
       * Get the YUV format
       *
       *
       * @return int
       */
      public int getYuv() {
          return mDimYuv;
      }
  
      /**
       * Return if the Type has a mipmap chain.
       *
       * @return boolean
       */
      public boolean hasMipmaps() {
          return mDimMipmaps;
      }
  
      /**
       * Return if the Type is a cube map.
       *
       * @return boolean
       */
      public boolean hasFaces() {
          return mDimFaces;
      }
  
      /**
       * Return the total number of accessable cells in the Type.
       *
       * @return int
       */
      public int getCount() {
          return mElementCount;
      }
  
      /**
       * @hide
        * Return the dimension of the specified array.
        *
        * @param arrayNum  The array dimension to query
        * @return int
        */
      public int getArray(int arrayNum) {
          if ((arrayNum < 0) || (arrayNum >= mMaxArrays)) {
              throw new RSIllegalArgumentException("Array dimension out of range.");
          }
  
          if (mArrays == null || arrayNum >= mArrays.length) {
              // Dimension in range but no array for that dimension allocated
              return 0;
          }
  
          return mArrays[arrayNum];
      }
  
      /**
       * @hide
        * Return the number of array dimensions.
        *
        * @return int
        */
      public int getArrayCount() {
          if (mArrays != null) return mArrays.length;
          return 0;
      }
  
      void calcElementCount() {
          boolean hasLod = hasMipmaps();
          int x = getX();
          int y = getY();
          int z = getZ();
          int faces = 1;
          if (hasFaces()) {
              faces = 6;
          }
          if (x == 0) {
              x = 1;
          }
          if (y == 0) {
              y = 1;
          }
          if (z == 0) {
              z = 1;
          }
  
          int count = x * y * z * faces;
  
          while (hasLod && ((x > 1) || (y > 1) || (z > 1))) {
              if(x > 1) {
                  x >>= 1;
              }
              if(y > 1) {
                  y >>= 1;
              }
              if(z > 1) {
                  z >>= 1;
              }
  
              count += x * y * z * faces;
          }
  
          if (mArrays != null) {
              for (int ct = 0; ct < mArrays.length; ct++) {
                  count *= mArrays[ct];
              }
          }
  
          mElementCount = count;
      }
  
  
      Type(long id, RenderScript rs) {
          super(id, rs);
      }
  
      @Override
      void updateFromNative() {
          // We have 6 integer/long to obtain mDimX; mDimY; mDimZ;
          // mDimLOD; mDimFaces; mElement;
          long[] dataBuffer = new long[6];
          mRS.nTypeGetNativeData(getID(mRS), dataBuffer);
  
          mDimX = (int)dataBuffer[0];
          mDimY = (int)dataBuffer[1];
          mDimZ = (int)dataBuffer[2];
          mDimMipmaps = dataBuffer[3] == 1 ? true : false;
          mDimFaces = dataBuffer[4] == 1 ? true : false;
  
          long elementID = dataBuffer[5];
          if(elementID != 0) {
              mElement = new Element(elementID, mRS);
              mElement.updateFromNative();
          }
          calcElementCount();
      }
  
      /**
       * Utility function for creating basic 1D types. The type is
       * created without mipmaps enabled.
       *
       * @param rs The RenderScript context
       * @param e The Element for the Type
       * @param dimX The X dimension, must be > 0
       *
       * @return Type
       */
      static public Type createX(RenderScript rs, Element e, int dimX) {
          if (dimX < 1) {
              throw new RSInvalidStateException("Dimension must be >= 1.");
          }
  
          long id = rs.nTypeCreate(e.getID(rs), dimX, 0, 0, false, false, 0);
          Type t = new Type(id, rs);
          t.mElement = e;
          t.mDimX = dimX;
          t.calcElementCount();
          return t;
      }
  
      /**
       * Utility function for creating basic 2D types. The type is
       * created without mipmaps or cubemaps.
       *
       * @param rs The RenderScript context
       * @param e The Element for the Type
       * @param dimX The X dimension, must be > 0
       * @param dimY The Y dimension, must be > 0
       *
       * @return Type
       */
      static public Type createXY(RenderScript rs, Element e, int dimX, int dimY) {
          if ((dimX < 1) || (dimY < 1)) {
              throw new RSInvalidStateException("Dimension must be >= 1.");
          }
  
          long id = rs.nTypeCreate(e.getID(rs), dimX, dimY, 0, false, false, 0);
          Type t = new Type(id, rs);
          t.mElement = e;
          t.mDimX = dimX;
          t.mDimY = dimY;
          t.calcElementCount();
          return t;
      }
  
      /**
       * Utility function for creating basic 3D types. The type is
       * created without mipmaps.
       *
       * @param rs The RenderScript context
       * @param e The Element for the Type
       * @param dimX The X dimension, must be > 0
       * @param dimY The Y dimension, must be > 0
       * @param dimZ The Z dimension, must be > 0
       *
       * @return Type
       */
      static public Type createXYZ(RenderScript rs, Element e, int dimX, int dimY, int dimZ) {
          if ((dimX < 1) || (dimY < 1) || (dimZ < 1)) {
              throw new RSInvalidStateException("Dimension must be >= 1.");
          }
  
          long id = rs.nTypeCreate(e.getID(rs), dimX, dimY, dimZ, false, false, 0);
          Type t = new Type(id, rs);
          t.mElement = e;
          t.mDimX = dimX;
          t.mDimY = dimY;
          t.mDimZ = dimZ;
          t.calcElementCount();
          return t;
      }
  
      /**
       * Builder class for Type.
       *
       */
      public static class Builder {
          RenderScript mRS;
          int mDimX = 1;
          int mDimY;
          int mDimZ;
          boolean mDimMipmaps;
          boolean mDimFaces;
          int mYuv;
          int[] mArray = new int[mMaxArrays];
  
          Element mElement;
  
          /**
           * Create a new builder object.
           *
           * @param rs
           * @param e The element for the type to be created.
           */
          public Builder(RenderScript rs, Element e) {
              e.checkValid();
              mRS = rs;
              mElement = e;
          }
  
          /**
           * Add a dimension to the Type.
           *
           *
           * @param value
           */
          public Builder setX(int value) {
              if(value < 1) {
                  throw new RSIllegalArgumentException("Values of less than 1 for Dimension X are not valid.");
              }
              mDimX = value;
              return this;
          }
  
          public Builder setY(int value) {
              if(value < 1) {
                  throw new RSIllegalArgumentException("Values of less than 1 for Dimension Y are not valid.");
              }
              mDimY = value;
              return this;
          }
  
          public Builder setZ(int value) {
              if(value < 1) {
                  throw new RSIllegalArgumentException("Values of less than 1 for Dimension Z are not valid.");
              }
              mDimZ = value;
              return this;
          }
  
          /**
           * @hide
           * Adds an array dimension to the builder
           *
           * @param dim
           * @param value
           *
           * @return Builder
           */
          public Builder setArray(int dim, int value) {
              if(dim < 0 || dim >= mMaxArrays) {
                  throw new RSIllegalArgumentException("Array dimension out of range.");
              }
              mArray[dim] = value;
              return this;
          }
  
          public Builder setMipmaps(boolean value) {
              mDimMipmaps = value;
              return this;
          }
  
          public Builder setFaces(boolean value) {
              mDimFaces = value;
              return this;
          }
  
          /**
           * Set the YUV layout for a Type.
           *
           * @param yuvFormat {@link android.graphics.ImageFormat#YV12}, {@link android.graphics.ImageFormat#NV21}, or
           * {@link android.graphics.ImageFormat#YUV_420_888}.
           */
          public Builder setYuvFormat(int yuvFormat) {
              switch (yuvFormat) {
              case android.graphics.ImageFormat.NV21:
              case android.graphics.ImageFormat.YV12:
              case android.graphics.ImageFormat.YUV_420_888:
                  break;
  
              default:
                  throw new RSIllegalArgumentException(
                      "Only ImageFormat.NV21, .YV12, and .YUV_420_888 are supported..");
              }
  
              mYuv = yuvFormat;
              return this;
          }
  
  
          /**
           * Validate structure and create a new Type.
           *
           * @return Type
           */
          public Type create() {
              if (mDimZ > 0) {
                  if ((mDimX < 1) || (mDimY < 1)) {
                      throw new RSInvalidStateException("Both X and Y dimension required when Z is present.");
                  }
                  if (mDimFaces) {
                      throw new RSInvalidStateException("Cube maps not supported with 3D types.");
                  }
              }
              if (mDimY > 0) {
                  if (mDimX < 1) {
                      throw new RSInvalidStateException("X dimension required when Y is present.");
                  }
              }
              if (mDimFaces) {
                  if (mDimY < 1) {
                      throw new RSInvalidStateException("Cube maps require 2D Types.");
                  }
              }
  
              if (mYuv != 0) {
                  if ((mDimZ != 0) || mDimFaces || mDimMipmaps) {
                      throw new RSInvalidStateException("YUV only supports basic 2D.");
                  }
              }
  
              int[] arrays = null;
              for (int ct = mMaxArrays - 1; ct >= 0; ct--) {
                  if (mArray[ct] != 0 && arrays == null) {
                      arrays = new int[ct];
                  }
                  if ((mArray[ct] == 0) && (arrays != null)) {
                      throw new RSInvalidStateException("Array dimensions must be contigous from 0.");
                  }
              }
  
              long id = mRS.nTypeCreate(mElement.getID(mRS),
                                       mDimX, mDimY, mDimZ, mDimMipmaps, mDimFaces, mYuv);
              Type t = new Type(id, mRS);
              t.mElement = mElement;
              t.mDimX = mDimX;
              t.mDimY = mDimY;
              t.mDimZ = mDimZ;
              t.mDimMipmaps = mDimMipmaps;
              t.mDimFaces = mDimFaces;
              t.mDimYuv = mYuv;
              t.mArrays = arrays;
  
              t.calcElementCount();
              return t;
          }
      }
  
  }
  