Alan Viverette | 3da604b | 2020-06-10 18:34:39 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2006 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.compat.annotation.UnsupportedAppUsage; |
| 20 | |
| 21 | /** |
| 22 | * The NinePatch class permits drawing a bitmap in nine or more sections. |
| 23 | * Essentially, it allows the creation of custom graphics that will scale the |
| 24 | * way that you define, when content added within the image exceeds the normal |
| 25 | * bounds of the graphic. For a thorough explanation of a NinePatch image, |
| 26 | * read the discussion in the |
| 27 | * <a href="{@docRoot}guide/topics/graphics/2d-graphics.html#nine-patch">2D |
| 28 | * Graphics</a> document. |
| 29 | * <p> |
| 30 | * The <a href="{@docRoot}guide/developing/tools/draw9patch.html">Draw 9-Patch</a> |
| 31 | * tool offers an extremely handy way to create your NinePatch images, |
| 32 | * using a WYSIWYG graphics editor. |
| 33 | * </p> |
| 34 | */ |
| 35 | public class NinePatch { |
| 36 | /** |
| 37 | * Struct of inset information attached to a 9 patch bitmap. |
| 38 | * |
| 39 | * Present on a 9 patch bitmap if it optical insets were manually included, |
| 40 | * or if outline insets were automatically included by aapt. |
| 41 | * |
| 42 | * @hide |
| 43 | */ |
| 44 | public static class InsetStruct { |
| 45 | @SuppressWarnings({"UnusedDeclaration"}) // called from JNI |
| 46 | @UnsupportedAppUsage |
| 47 | InsetStruct(int opticalLeft, int opticalTop, int opticalRight, int opticalBottom, |
| 48 | int outlineLeft, int outlineTop, int outlineRight, int outlineBottom, |
| 49 | float outlineRadius, int outlineAlpha, float decodeScale) { |
| 50 | opticalRect = new Rect(opticalLeft, opticalTop, opticalRight, opticalBottom); |
| 51 | opticalRect.scale(decodeScale); |
| 52 | |
| 53 | outlineRect = scaleInsets(outlineLeft, outlineTop, |
| 54 | outlineRight, outlineBottom, decodeScale); |
| 55 | |
| 56 | this.outlineRadius = outlineRadius * decodeScale; |
| 57 | this.outlineAlpha = outlineAlpha / 255.0f; |
| 58 | } |
| 59 | |
| 60 | public final Rect opticalRect; |
| 61 | public final Rect outlineRect; |
| 62 | public final float outlineRadius; |
| 63 | public final float outlineAlpha; |
| 64 | |
| 65 | /** |
| 66 | * Scales up the rect by the given scale, ceiling values, so actual outline Rect |
| 67 | * grows toward the inside. |
| 68 | */ |
| 69 | public static Rect scaleInsets(int left, int top, int right, int bottom, float scale) { |
| 70 | if (scale == 1.0f) { |
| 71 | return new Rect(left, top, right, bottom); |
| 72 | } |
| 73 | |
| 74 | Rect result = new Rect(); |
| 75 | result.left = (int) Math.ceil(left * scale); |
| 76 | result.top = (int) Math.ceil(top * scale); |
| 77 | result.right = (int) Math.ceil(right * scale); |
| 78 | result.bottom = (int) Math.ceil(bottom * scale); |
| 79 | return result; |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | @UnsupportedAppUsage |
| 84 | private final Bitmap mBitmap; |
| 85 | |
| 86 | /** |
| 87 | * Used by native code. This pointer is an instance of Res_png_9patch*. |
| 88 | * |
| 89 | * @hide |
| 90 | */ |
| 91 | @UnsupportedAppUsage |
| 92 | public long mNativeChunk; |
| 93 | |
| 94 | private Paint mPaint; |
| 95 | private String mSrcName; |
| 96 | |
| 97 | /** |
| 98 | * Create a drawable projection from a bitmap to nine patches. |
| 99 | * |
| 100 | * @param bitmap The bitmap describing the patches. |
| 101 | * @param chunk The 9-patch data chunk describing how the underlying bitmap |
| 102 | * is split apart and drawn. |
| 103 | */ |
| 104 | public NinePatch(Bitmap bitmap, byte[] chunk) { |
| 105 | this(bitmap, chunk, null); |
| 106 | } |
| 107 | |
| 108 | /** |
| 109 | * Create a drawable projection from a bitmap to nine patches. |
| 110 | * |
| 111 | * @param bitmap The bitmap describing the patches. |
| 112 | * @param chunk The 9-patch data chunk describing how the underlying |
| 113 | * bitmap is split apart and drawn. |
| 114 | * @param srcName The name of the source for the bitmap. Might be null. |
| 115 | */ |
| 116 | public NinePatch(Bitmap bitmap, byte[] chunk, String srcName) { |
| 117 | mBitmap = bitmap; |
| 118 | mSrcName = srcName; |
| 119 | mNativeChunk = validateNinePatchChunk(chunk); |
| 120 | } |
| 121 | |
| 122 | /** |
| 123 | * @hide |
| 124 | */ |
| 125 | public NinePatch(NinePatch patch) { |
| 126 | mBitmap = patch.mBitmap; |
| 127 | mSrcName = patch.mSrcName; |
| 128 | if (patch.mPaint != null) { |
| 129 | mPaint = new Paint(patch.mPaint); |
| 130 | } |
| 131 | // No need to validate the 9patch chunk again, it was done by |
| 132 | // the instance we're copying from |
| 133 | mNativeChunk = patch.mNativeChunk; |
| 134 | } |
| 135 | |
| 136 | @Override |
| 137 | protected void finalize() throws Throwable { |
| 138 | try { |
| 139 | if (mNativeChunk != 0) { |
| 140 | // only attempt to destroy correctly initilized chunks |
| 141 | nativeFinalize(mNativeChunk); |
| 142 | mNativeChunk = 0; |
| 143 | } |
| 144 | } finally { |
| 145 | super.finalize(); |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | /** |
| 150 | * Returns the name of this NinePatch object if one was specified |
| 151 | * when calling the constructor. |
| 152 | */ |
| 153 | public String getName() { |
| 154 | return mSrcName; |
| 155 | } |
| 156 | |
| 157 | /** |
| 158 | * Returns the paint used to draw this NinePatch. The paint can be null. |
| 159 | * |
| 160 | * @see #setPaint(Paint) |
| 161 | * @see #draw(Canvas, Rect) |
| 162 | * @see #draw(Canvas, RectF) |
| 163 | */ |
| 164 | public Paint getPaint() { |
| 165 | return mPaint; |
| 166 | } |
| 167 | |
| 168 | /** |
| 169 | * Sets the paint to use when drawing the NinePatch. |
| 170 | * |
| 171 | * @param p The paint that will be used to draw this NinePatch. |
| 172 | * |
| 173 | * @see #getPaint() |
| 174 | * @see #draw(Canvas, Rect) |
| 175 | * @see #draw(Canvas, RectF) |
| 176 | */ |
| 177 | public void setPaint(Paint p) { |
| 178 | mPaint = p; |
| 179 | } |
| 180 | |
| 181 | /** |
| 182 | * Returns the bitmap used to draw this NinePatch. |
| 183 | */ |
| 184 | public Bitmap getBitmap() { |
| 185 | return mBitmap; |
| 186 | } |
| 187 | |
| 188 | /** |
| 189 | * Draws the NinePatch. This method will use the paint returned by {@link #getPaint()}. |
| 190 | * |
| 191 | * @param canvas A container for the current matrix and clip used to draw the NinePatch. |
| 192 | * @param location Where to draw the NinePatch. |
| 193 | */ |
| 194 | public void draw(Canvas canvas, RectF location) { |
| 195 | canvas.drawPatch(this, location, mPaint); |
| 196 | } |
| 197 | |
| 198 | /** |
| 199 | * Draws the NinePatch. This method will use the paint returned by {@link #getPaint()}. |
| 200 | * |
| 201 | * @param canvas A container for the current matrix and clip used to draw the NinePatch. |
| 202 | * @param location Where to draw the NinePatch. |
| 203 | */ |
| 204 | public void draw(Canvas canvas, Rect location) { |
| 205 | canvas.drawPatch(this, location, mPaint); |
| 206 | } |
| 207 | |
| 208 | /** |
| 209 | * Draws the NinePatch. This method will ignore the paint returned |
| 210 | * by {@link #getPaint()} and use the specified paint instead. |
| 211 | * |
| 212 | * @param canvas A container for the current matrix and clip used to draw the NinePatch. |
| 213 | * @param location Where to draw the NinePatch. |
| 214 | * @param paint The Paint to draw through. |
| 215 | */ |
| 216 | public void draw(Canvas canvas, Rect location, Paint paint) { |
| 217 | canvas.drawPatch(this, location, paint); |
| 218 | } |
| 219 | |
| 220 | /** |
| 221 | * Return the underlying bitmap's density, as per |
| 222 | * {@link Bitmap#getDensity() Bitmap.getDensity()}. |
| 223 | */ |
| 224 | public int getDensity() { |
| 225 | return mBitmap.mDensity; |
| 226 | } |
| 227 | |
| 228 | /** |
| 229 | * Returns the intrinsic width, in pixels, of this NinePatch. This is equivalent |
| 230 | * to querying the width of the underlying bitmap returned by {@link #getBitmap()}. |
| 231 | */ |
| 232 | public int getWidth() { |
| 233 | return mBitmap.getWidth(); |
| 234 | } |
| 235 | |
| 236 | /** |
| 237 | * Returns the intrinsic height, in pixels, of this NinePatch. This is equivalent |
| 238 | * to querying the height of the underlying bitmap returned by {@link #getBitmap()}. |
| 239 | */ |
| 240 | public int getHeight() { |
| 241 | return mBitmap.getHeight(); |
| 242 | } |
| 243 | |
| 244 | /** |
| 245 | * Indicates whether this NinePatch contains transparent or translucent pixels. |
| 246 | * This is equivalent to calling <code>getBitmap().hasAlpha()</code> on this |
| 247 | * NinePatch. |
| 248 | */ |
| 249 | public final boolean hasAlpha() { |
| 250 | return mBitmap.hasAlpha(); |
| 251 | } |
| 252 | |
| 253 | /** |
| 254 | * Returns a {@link Region} representing the parts of the NinePatch that are |
| 255 | * completely transparent. |
| 256 | * |
| 257 | * @param bounds The location and size of the NinePatch. |
| 258 | * |
| 259 | * @return null if the NinePatch has no transparent region to |
| 260 | * report, else a {@link Region} holding the parts of the specified bounds |
| 261 | * that are transparent. |
| 262 | */ |
| 263 | public final Region getTransparentRegion(Rect bounds) { |
| 264 | long r = nativeGetTransparentRegion(mBitmap.getNativeInstance(), |
| 265 | mNativeChunk, bounds); |
| 266 | return r != 0 ? new Region(r) : null; |
| 267 | } |
| 268 | |
| 269 | /** |
| 270 | * Verifies that the specified byte array is a valid 9-patch data chunk. |
| 271 | * |
| 272 | * @param chunk A byte array representing a 9-patch data chunk. |
| 273 | * |
| 274 | * @return True if the specified byte array represents a 9-patch data chunk, |
| 275 | * false otherwise. |
| 276 | */ |
| 277 | public native static boolean isNinePatchChunk(byte[] chunk); |
| 278 | |
| 279 | /** |
| 280 | * Validates the 9-patch chunk and throws an exception if the chunk is invalid. |
| 281 | * If validation is successful, this method returns a native Res_png_9patch* |
| 282 | * object used by the renderers. |
| 283 | */ |
| 284 | private static native long validateNinePatchChunk(byte[] chunk); |
| 285 | private static native void nativeFinalize(long chunk); |
| 286 | private static native long nativeGetTransparentRegion(long bitmapHandle, long chunk, |
| 287 | Rect location); |
| 288 | } |