1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.ui.resources.statics;
6 
7 import android.graphics.Bitmap;
8 import android.graphics.NinePatch;
9 import android.graphics.Rect;
10 
11 import java.nio.BufferUnderflowException;
12 import java.nio.ByteBuffer;
13 import java.nio.ByteOrder;
14 
15 /**
16  * A helper class to decode and expose relevant 9-patch data from a Bitmap.
17  */
18 public class NinePatchData {
19     private final int mWidth;
20     private final int mHeight;
21     private final Rect mPadding;
22     private final int[] mDivX;
23     private final int[] mDivY;
24 
25     private Rect mAperture;
26 
27     /**
28      * Creates a {@link NinePatchData} that stores 9-patch metadata.
29      * @param width   The width of the underlying bitmap.
30      * @param height  The height of the underlying bitmap.
31      * @param padding The padding of the 9-patch for the content area.  This padding is a set of
32      *                insets (left = left padding, top = top padding, right = right padding,
33      *                bottom = bottom padding).
34      * @param divX    A run-length encoded list of stretch regions along the x dimension.  The
35      *                regions will go from 0 -> divX[0] - 1, divX[0] -> divX[1] - 1, etc..
36      * @param divY    A run-length encoded list of stretch regions along the y dimension.  The
37      *                regions will go from 0 -> divY[0] - 1, divY[0] -> divY[1] - 1, etc..
38      */
NinePatchData(int width, int height, Rect padding, int[] divX, int[] divY)39     private NinePatchData(int width, int height, Rect padding, int[] divX, int[] divY) {
40         mWidth = width;
41         mHeight = height;
42         mPadding = new Rect(
43                 padding.left, padding.top, mWidth - padding.right, mHeight - padding.bottom);
44 
45         mDivX = new int[divX.length];
46         mDivY = new int[divY.length];
47 
48         System.arraycopy(divX, 0, mDivX, 0, divX.length);
49         System.arraycopy(divY, 0, mDivY, 0, divY.length);
50 
51         mAperture = new Rect(mDivX[0], mDivY[0], mDivX[1], mDivY[1]);
52     }
53 
54     /**
55      * @return The padded content area of this 9-patch.
56      */
getPadding()57     public Rect getPadding() {
58         return mPadding;
59     }
60 
61     /**
62      * This class only exposes one 9-patch stretch region.
63      * @return The aperture of this 9-patch.  This specifies the center of the 9-patch, with the
64      *         surrounding areas being stretchable.
65      */
getAperture()66     public Rect getAperture() {
67         return mAperture;
68     }
69 
70     /**
71      * Attempts to decode 9-patch data from a {@link Bitmap}.
72      * @param bitmap The {@link Bitmap} to check.
73      * @return       An instance of {@link NinePatchData} representing the 9-patch information
74      *               encoded in {@code bitmap} or {@code null} if the {@link Bitmap} wasn't a
75      *               9-patch.
76      */
create(Bitmap bitmap)77     public static NinePatchData create(Bitmap bitmap) {
78         if (bitmap == null) return null;
79 
80         try {
81             byte[] chunk = bitmap.getNinePatchChunk();
82             if (chunk == null || !NinePatch.isNinePatchChunk(chunk)) return null;
83 
84             ByteBuffer buffer = ByteBuffer.wrap(chunk).order(ByteOrder.nativeOrder());
85 
86             // int8_t wasDeserialized
87             if (buffer.get() == 0) return null;
88 
89             // int8_t numXDivs
90             int numDivX = buffer.get();
91             if (numDivX == 0 || (numDivX & 0x01) != 0) return null;
92 
93             // int8_t numYDivs
94             int numDivY = buffer.get();
95             if (numDivY == 0 || (numDivY & 0x01) != 0) return null;
96 
97             // int8_t numColors
98             buffer.get();
99 
100             // uint32_t xDivsOffset
101             buffer.getInt();
102 
103             // uint32_t yDivsOffset
104             buffer.getInt();
105 
106             Rect padding = new Rect();
107 
108             // uint32_t paddingLeft
109             padding.left = buffer.getInt();
110 
111             // uint32_t paddingRight
112             padding.right = buffer.getInt();
113 
114             // uint32_t paddingTop
115             padding.top = buffer.getInt();
116 
117             // uint32_t paddingBottom
118             padding.bottom = buffer.getInt();
119 
120             // uint32_t colorsOffset
121             buffer.getInt();
122 
123             // uint32_t uint32_t uint32_t ...
124             int[] divX = new int[numDivX];
125             for (int i = 0; i < numDivX; i++) divX[i] = buffer.getInt();
126 
127             // uint32_t uint32_t uint32_t ...
128             int[] divY = new int[numDivY];
129             for (int i = 0; i < numDivY; i++) divY[i] = buffer.getInt();
130 
131             return new NinePatchData(bitmap.getWidth(), bitmap.getHeight(), padding, divX, divY);
132         } catch (BufferUnderflowException ex) {
133             return null;
134         }
135     }
136 }