1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.graphics; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.IntDef; 21 import android.annotation.IntRange; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 25 import com.android.graphics.hwui.flags.Flags; 26 27 import java.lang.annotation.Retention; 28 import java.lang.annotation.RetentionPolicy; 29 30 /** 31 * Shader used to draw a bitmap as a texture. The bitmap can be repeated or 32 * mirrored by setting the tiling mode. 33 */ 34 public class BitmapShader extends Shader { 35 /** 36 * Prevent garbage collection. 37 */ 38 /*package*/ Bitmap mBitmap; 39 private Gainmap mOverrideGainmap; 40 41 private int mTileX; 42 private int mTileY; 43 44 /** @hide */ 45 @IntDef(prefix = {"FILTER_MODE"}, value = { 46 FILTER_MODE_DEFAULT, 47 FILTER_MODE_NEAREST, 48 FILTER_MODE_LINEAR 49 }) 50 @Retention(RetentionPolicy.SOURCE) 51 public @interface FilterMode {} 52 53 /** 54 * This FilterMode value will respect the value of the Paint#isFilterBitmap flag while the 55 * shader is attached to the Paint. 56 * 57 * <p>The exception to this rule is when a Shader is attached as input to a RuntimeShader. In 58 * that case this mode will default to FILTER_MODE_NEAREST.</p> 59 * 60 * @see #setFilterMode(int) 61 */ 62 public static final int FILTER_MODE_DEFAULT = 0; 63 /** 64 * This FilterMode value will cause the shader to sample from the nearest pixel to the requested 65 * sample point. 66 * 67 * <p>This value will override the effect of Paint#isFilterBitmap.</p> 68 * 69 * @see #setFilterMode(int) 70 */ 71 public static final int FILTER_MODE_NEAREST = 1; 72 /** 73 * This FilterMode value will cause the shader to interpolate the output of the shader from a 74 * 2x2 grid of pixels nearest to the sample point (i.e. bilinear interpolation). 75 * 76 * <p>This value will override the effect of Paint#isFilterBitmap.</p> 77 * 78 * @see #setFilterMode(int) 79 */ 80 public static final int FILTER_MODE_LINEAR = 2; 81 82 @FilterMode 83 private int mFilterMode; 84 85 /* 86 * This is cache of the last value from the Paint of bitmap-filtering. 87 * In the future, BitmapShaders will carry their own (expanded) data for this 88 * (e.g. including mipmap options, or bicubic weights) 89 * 90 * When that happens, this bool will become those extended values, and we will 91 * need to track whether this Shader was created with those new constructors, 92 * or from the current "legacy" constructor, which (for compatibility) will 93 * still need to know the Paint's setting. 94 * 95 * When the filter Paint setting is finally gone, we will be able to remove 96 * the filterFromPaint parameter currently being passed to createNativeInstance() 97 * and shouldDiscardNativeInstance(), as shaders will always know their filter 98 * settings. 99 */ 100 private boolean mFilterFromPaint; 101 102 /** 103 * Stores whether or not the contents of this shader's bitmap will be sampled 104 * without modification or if the bitmap's properties, like colorspace and 105 * premultiplied alpha, will be respected when sampling from the bitmap's buffer. 106 */ 107 private boolean mIsDirectSampled; 108 109 private boolean mRequestDirectSampling; 110 111 private int mMaxAniso = 0; 112 113 /** 114 * Call this to create a new shader that will draw with a bitmap. 115 * 116 * @param bitmap The bitmap to use inside the shader 117 * @param tileX The tiling mode for x to draw the bitmap in. 118 * @param tileY The tiling mode for y to draw the bitmap in. 119 */ BitmapShader(@onNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY)120 public BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY) { 121 this(bitmap, tileX.nativeInt, tileY.nativeInt); 122 } 123 BitmapShader(Bitmap bitmap, int tileX, int tileY)124 private BitmapShader(Bitmap bitmap, int tileX, int tileY) { 125 if (bitmap == null) { 126 throw new IllegalArgumentException("Bitmap must be non-null"); 127 } 128 bitmap.checkRecycled("Cannot create BitmapShader for recycled bitmap"); 129 mBitmap = bitmap; 130 mTileX = tileX; 131 mTileY = tileY; 132 mFilterMode = FILTER_MODE_DEFAULT; 133 mFilterFromPaint = false; 134 mIsDirectSampled = false; 135 mRequestDirectSampling = false; 136 } 137 138 /** 139 * Returns the filter mode used when sampling from this shader 140 */ 141 @FilterMode getFilterMode()142 public int getFilterMode() { 143 return mFilterMode; 144 } 145 146 /** 147 * Set the filter mode to be used when sampling from this shader. If this is configured 148 * then the anisotropic filtering value specified in any previous call to 149 * {@link #setMaxAnisotropy(int)} is ignored. 150 */ setFilterMode(@ilterMode int mode)151 public void setFilterMode(@FilterMode int mode) { 152 if (mode != mFilterMode) { 153 mFilterMode = mode; 154 mMaxAniso = 0; 155 discardNativeInstance(); 156 } 157 } 158 159 /** 160 * Enables and configures the max anisotropy sampling value. If this value is configured, 161 * {@link #setFilterMode(int)} is ignored. 162 * 163 * Anisotropic filtering can enhance visual quality by removing aliasing effects of images 164 * that are at oblique viewing angles. This value is typically consumed as a power of 2 and 165 * anisotropic values of the next power of 2 typically provide twice the quality improvement 166 * as the previous value. For example, a sampling value of 4 would provide twice the improvement 167 * of a sampling value of 2. It is important to note that higher sampling values reach 168 * diminishing returns as the improvements between 8 and 16 can be slight. 169 * 170 * @param maxAnisotropy The Anisotropy value to use for filtering. Must be greater than 0. 171 */ setMaxAnisotropy(@ntRangefrom = 1) int maxAnisotropy)172 public void setMaxAnisotropy(@IntRange(from = 1) int maxAnisotropy) { 173 if (mMaxAniso != maxAnisotropy && maxAnisotropy > 0) { 174 mMaxAniso = maxAnisotropy; 175 mFilterMode = FILTER_MODE_DEFAULT; 176 discardNativeInstance(); 177 } 178 } 179 180 /** 181 * Draws the BitmapShader with a copy of the given gainmap instead of the gainmap on the Bitmap 182 * the shader was constructed from 183 * 184 * @param overrideGainmap The gainmap to draw instead, null to use any gainmap on the Bitmap 185 */ 186 @FlaggedApi(Flags.FLAG_GAINMAP_ANIMATIONS) setOverrideGainmap(@ullable Gainmap overrideGainmap)187 public void setOverrideGainmap(@Nullable Gainmap overrideGainmap) { 188 if (!Flags.gainmapAnimations()) throw new IllegalStateException("API not available"); 189 190 if (overrideGainmap == null) { 191 mOverrideGainmap = null; 192 } else { 193 mOverrideGainmap = new Gainmap(overrideGainmap, overrideGainmap.getGainmapContents()); 194 } 195 discardNativeInstance(); 196 } 197 198 /** 199 * Returns the current max anisotropic filtering value configured by 200 * {@link #setFilterMode(int)}. If {@link #setFilterMode(int)} is invoked this returns zero. 201 */ getMaxAnisotropy()202 public int getMaxAnisotropy() { 203 return mMaxAniso; 204 } 205 206 /** @hide */ getNativeInstanceWithDirectSampling()207 /* package */ synchronized long getNativeInstanceWithDirectSampling() { 208 mRequestDirectSampling = true; 209 return getNativeInstance(); 210 } 211 212 /** @hide */ 213 @Override createNativeInstance(long nativeMatrix, boolean filterFromPaint)214 protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) { 215 mBitmap.checkRecycled("BitmapShader's bitmap has been recycled"); 216 217 boolean enableLinearFilter = mFilterMode == FILTER_MODE_LINEAR; 218 if (mFilterMode == FILTER_MODE_DEFAULT) { 219 mFilterFromPaint = filterFromPaint; 220 enableLinearFilter = mFilterFromPaint; 221 } 222 223 mIsDirectSampled = mRequestDirectSampling; 224 mRequestDirectSampling = false; 225 return nativeCreate(nativeMatrix, mBitmap.getNativeInstance(), mTileX, 226 mTileY, mMaxAniso, enableLinearFilter, mIsDirectSampled, 227 mOverrideGainmap != null ? mOverrideGainmap.mNativePtr : 0); 228 } 229 230 /** @hide */ 231 @Override shouldDiscardNativeInstance(boolean filterFromPaint)232 protected boolean shouldDiscardNativeInstance(boolean filterFromPaint) { 233 return mIsDirectSampled != mRequestDirectSampling 234 || (mFilterMode == FILTER_MODE_DEFAULT && mFilterFromPaint != filterFromPaint); 235 } 236 nativeCreate(long nativeMatrix, long bitmapHandle, int shaderTileModeX, int shaderTileModeY, int maxAniso, boolean filter, boolean isDirectSampled, long overrideGainmapHandle)237 private static native long nativeCreate(long nativeMatrix, long bitmapHandle, 238 int shaderTileModeX, int shaderTileModeY, int maxAniso, boolean filter, 239 boolean isDirectSampled, long overrideGainmapHandle); 240 } 241 242