content: /*
   * Copyright (C) 2006 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.graphics;
  
  import android.annotation.ColorInt;
  import android.annotation.ColorLong;
  import android.annotation.NonNull;
  import android.annotation.Nullable;
  import android.annotation.UnsupportedAppUsage;
  
  import libcore.util.NativeAllocationRegistry;
  
  /**
   * Shader is the based class for objects that return horizontal spans of colors
   * during drawing. A subclass of Shader is installed in a Paint calling
   * paint.setShader(shader). After that any object (other than a bitmap) that is
   * drawn with that paint will get its color(s) from the shader.
   */
  public class Shader {
  
      private static class NoImagePreloadHolder {
          public static final NativeAllocationRegistry sRegistry =
                  NativeAllocationRegistry.createMalloced(
                  Shader.class.getClassLoader(), nativeGetFinalizer());
      }
  
      /**
       * @deprecated Use subclass constructors directly instead.
       */
      @Deprecated
      public Shader() {
          mColorSpace = null;
      }
  
      /**
       * @hide
       */
      public Shader(ColorSpace colorSpace) {
          mColorSpace = colorSpace;
          if (colorSpace == null) {
              throw new IllegalArgumentException(
                      "Use Shader() to create a Shader with no ColorSpace");
          }
  
          // This just ensures that if the ColorSpace is invalid, the Exception will be thrown now.
          mColorSpace.getNativeInstance();
      }
  
      private final ColorSpace mColorSpace;
  
      /**
       * @hide
       */
      protected ColorSpace colorSpace() {
          return mColorSpace;
      }
  
      /**
       * Current native shader instance. Created and updated lazily when {@link #getNativeInstance()}
       * is called - otherwise may be out of date with java setters/properties.
       */
      private long mNativeInstance;
      // Runnable to do immediate destruction
      private Runnable mCleaner;
  
      /**
       * Current matrix - always set to null if local matrix is identity.
       */
      private Matrix mLocalMatrix;
  
      public enum TileMode {
          /**
           * replicate the edge color if the shader draws outside of its
           * original bounds
           */
          CLAMP   (0),
          /**
           * repeat the shader's image horizontally and vertically
           */
          REPEAT  (1),
          /**
           * repeat the shader's image horizontally and vertically, alternating
           * mirror images so that adjacent images always seam
           */
          MIRROR  (2);
      
          TileMode(int nativeInt) {
              this.nativeInt = nativeInt;
          }
          @UnsupportedAppUsage
          final int nativeInt;
      }
  
      /**
       * Return true if the shader has a non-identity local matrix.
       * @param localM Set to the local matrix of the shader, if the shader's matrix is non-null.
       * @return true if the shader has a non-identity local matrix
       */
      public boolean getLocalMatrix(@NonNull Matrix localM) {
          if (mLocalMatrix != null) {
              localM.set(mLocalMatrix);
              return true; // presence of mLocalMatrix means it's not identity
          }
          return false;
      }
  
      /**
       * Set the shader's local matrix. Passing null will reset the shader's
       * matrix to identity. If the matrix has scale value as 0, the drawing
       * result is undefined.
       *
       * @param localM The shader's new local matrix, or null to specify identity
       */
      public void setLocalMatrix(@Nullable Matrix localM) {
          if (localM == null || localM.isIdentity()) {
              if (mLocalMatrix != null) {
                  mLocalMatrix = null;
                  discardNativeInstance();
              }
          } else {
              if (mLocalMatrix == null) {
                  mLocalMatrix = new Matrix(localM);
                  discardNativeInstance();
              } else if (!mLocalMatrix.equals(localM)) {
                  mLocalMatrix.set(localM);
                  discardNativeInstance();
              }
          }
      }
  
      long createNativeInstance(long nativeMatrix) {
          return 0;
      }
  
      /** @hide */
      protected final void discardNativeInstance() {
          if (mNativeInstance != 0) {
              mCleaner.run();
              mCleaner = null;
              mNativeInstance = 0;
          }
      }
  
      /**
       * Callback for subclasses to call {@link #discardNativeInstance()} if the most recently
       * constructed native instance is no longer valid.
       * @hide
       */
      protected void verifyNativeInstance() {
      }
  
  
      /**
       * @hide
       */
      public final long getNativeInstance() {
          // verify mNativeInstance is valid
          verifyNativeInstance();
  
          if (mNativeInstance == 0) {
              mNativeInstance = createNativeInstance(mLocalMatrix == null
                      ? 0 : mLocalMatrix.native_instance);
              if (mNativeInstance != 0) {
                  mCleaner = NoImagePreloadHolder.sRegistry.registerNativeAllocation(
                          this, mNativeInstance);
              }
          }
          return mNativeInstance;
      }
  
      /**
       * @hide
       */
      public static @ColorLong long[] convertColors(@NonNull @ColorInt int[] colors) {
          if (colors.length < 2) {
              throw new IllegalArgumentException("needs >= 2 number of colors");
          }
  
          long[] colorLongs = new long[colors.length];
          for (int i = 0; i < colors.length; ++i) {
              colorLongs[i] = Color.pack(colors[i]);
          }
  
          return colorLongs;
      }
  
      /**
       * Detect the ColorSpace that the {@code colors} share.
       *
       * @throws IllegalArgumentException if the colors do not all share the same,
       *      valid ColorSpace, or if there are less than 2 colors.
       *
       * @hide
       */
      public static ColorSpace detectColorSpace(@NonNull @ColorLong long[] colors) {
          if (colors.length < 2) {
              throw new IllegalArgumentException("needs >= 2 number of colors");
          }
          final ColorSpace colorSpace = Color.colorSpace(colors[0]);
          for (int i = 1; i < colors.length; ++i) {
              if (Color.colorSpace(colors[i]) != colorSpace) {
                  throw new IllegalArgumentException("All colors must be in the same ColorSpace!");
              }
          }
          return colorSpace;
      }
  
      private static native long nativeGetFinalizer();
  
  }
  
  