| /* |
| * 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 = new NativeAllocationRegistry( |
| Shader.class.getClassLoader(), nativeGetFinalizer(), 50); |
| } |
| |
| /** |
| * @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(); |
| |
| } |
| |