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