• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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