blob: a6c6dfdbf30c5b33b4df22d05ca84288720036c2 [file] [log] [blame]
Xavier Ducrohete4d97872010-11-22 20:09:55 -08001/*
2 * Copyright (C) 2010 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
17package android.graphics;
18
Xavier Ducrohet3054fe62010-12-06 10:11:44 -080019import com.android.layoutlib.bridge.Bridge;
Xavier Ducrohete4d97872010-11-22 20:09:55 -080020import com.android.layoutlib.bridge.impl.DelegateManager;
21import com.android.ninepatch.NinePatchChunk;
22
23import android.graphics.drawable.NinePatchDrawable;
24
25import java.awt.Graphics2D;
26import java.awt.image.BufferedImage;
27import java.io.ByteArrayInputStream;
28import java.io.ByteArrayOutputStream;
29import java.io.IOException;
30import java.io.ObjectInputStream;
31import java.io.ObjectOutputStream;
32import java.lang.ref.SoftReference;
33import java.util.HashMap;
34import java.util.Map;
35
36/**
37 * Delegate implementing the native methods of android.graphics.NinePatch
38 *
39 * Through the layoutlib_create tool, the original native methods of NinePatch have been replaced
40 * by calls to methods of the same name in this delegate class.
41 *
42 * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
43 * around to map int to instance of the delegate.
44 *
45 */
46public class NinePatch_Delegate {
47
48 /**
49 * Cache map for {@link NinePatchChunk}.
50 * When the chunks are created they are serialized into a byte[], and both are put
51 * in the cache, using a {@link SoftReference} for the chunk. The default Java classes
52 * for {@link NinePatch} and {@link NinePatchDrawable} only reference to the byte[] data, and
53 * provide this for drawing.
54 * Using the cache map allows us to not have to deserialize the byte[] back into a
55 * {@link NinePatchChunk} every time a rendering is done.
56 */
57 private final static Map<byte[], SoftReference<NinePatchChunk>> sChunkCache =
58 new HashMap<byte[], SoftReference<NinePatchChunk>>();
59
60 // ---- Public Helper methods ----
61
62 /**
63 * Serializes the given chunk.
64 *
65 * @return the serialized data for the chunk.
66 */
67 public static byte[] serialize(NinePatchChunk chunk) {
68 // serialize the chunk to get a byte[]
69 ByteArrayOutputStream baos = new ByteArrayOutputStream();
70 ObjectOutputStream oos = null;
71 try {
72 oos = new ObjectOutputStream(baos);
73 oos.writeObject(chunk);
74 } catch (IOException e) {
Xavier Ducrohet3054fe62010-12-06 10:11:44 -080075 Bridge.getLog().error("Failed to serialize NinePatchChunk.", e);
Xavier Ducrohete4d97872010-11-22 20:09:55 -080076 return null;
77 } finally {
78 if (oos != null) {
79 try {
80 oos.close();
81 } catch (IOException e) {
82 }
83 }
84 }
85
86 // get the array and add it to the cache
87 byte[] array = baos.toByteArray();
88 sChunkCache.put(array, new SoftReference<NinePatchChunk>(chunk));
89 return array;
90 }
91
92 // ---- native methods ----
93
94 /*package*/ static boolean isNinePatchChunk(byte[] chunk) {
95 NinePatchChunk chunkObject = getChunk(chunk);
96 if (chunkObject != null) {
97 return true;
98 }
99
100 return false;
101 }
102
103 /*package*/ static void validateNinePatchChunk(int bitmap, byte[] chunk) {
104 // the default JNI implementation only checks that the byte[] has the same
105 // size as the C struct it represent. Since we cannot do the same check (serialization
106 // will return different size depending on content), we do nothing.
107 }
108
109 /*package*/ static void nativeDraw(int canvas_instance, RectF loc, int bitmap_instance,
110 byte[] c, int paint_instance_or_null, int destDensity, int srcDensity) {
111 draw(canvas_instance,
112 (int) loc.left, (int) loc.top, (int) loc.width(), (int) loc.height(),
113 bitmap_instance, c, paint_instance_or_null,
114 destDensity, srcDensity);
115 }
116
117 /*package*/ static void nativeDraw(int canvas_instance, Rect loc, int bitmap_instance,
118 byte[] c, int paint_instance_or_null, int destDensity, int srcDensity) {
119 draw(canvas_instance,
120 loc.left, loc.top, loc.width(), loc.height(),
121 bitmap_instance, c, paint_instance_or_null,
122 destDensity, srcDensity);
123 }
124
125 private static void draw(int canvas_instance,
126 int left, int top, int right, int bottom,
127 int bitmap_instance, byte[] c, int paint_instance_or_null,
128 int destDensity, int srcDensity) {
129 // get the delegate from the native int.
130 Bitmap_Delegate bitmap_delegate = Bitmap_Delegate.getDelegate(bitmap_instance);
131 if (bitmap_delegate == null) {
132 assert false;
133 return;
134 }
135
136 if (c == null) {
137 // not a 9-patch?
138 BufferedImage image = bitmap_delegate.getImage();
139 Canvas_Delegate.native_drawBitmap(canvas_instance, bitmap_instance,
140 new Rect(0, 0, image.getWidth(), image.getHeight()),
141 new Rect(left, top, right, bottom),
142 paint_instance_or_null, destDensity, srcDensity);
143 return;
144 }
145
146 NinePatchChunk chunkObject = getChunk(c);
147 assert chunkObject != null;
148 if (chunkObject == null) {
149 return;
150 }
151
152 Canvas_Delegate canvas_delegate = Canvas_Delegate.getDelegate(canvas_instance);
153 if (canvas_delegate == null) {
154 assert false;
155 return;
156 }
157
158 // this one can be null
159 Paint_Delegate paint_delegate = Paint_Delegate.getDelegate(paint_instance_or_null);
160
161 Graphics2D graphics;
162 if (paint_delegate != null) {
163 graphics = canvas_delegate.getCustomGraphics(paint_delegate);
164 } else {
165 graphics = canvas_delegate.getGraphics2d();
166 }
167
168 try {
169 chunkObject.draw(bitmap_delegate.getImage(), graphics,
170 left, top, right - left, bottom - top);
171 } finally {
172 if (paint_delegate != null) {
173 graphics.dispose();
174 }
175 }
176
177 }
178
179 /*package*/ static int nativeGetTransparentRegion(int bitmap, byte[] chunk, Rect location) {
180 return 0;
181 }
182
183 // ---- Private Helper methods ----
184
185 /**
186 * Returns a {@link NinePatchChunk} object for the given serialized representation.
187 *
188 * If the chunk is present in the cache then the object from the cache is returned, otherwise
189 * the array is deserialized into a {@link NinePatchChunk} object.
190 *
191 * @param array the serialized representation of the chunk.
192 * @return the NinePatchChunk or null if deserialization failed.
193 */
194 private static NinePatchChunk getChunk(byte[] array) {
195 SoftReference<NinePatchChunk> chunkRef = sChunkCache.get(array);
196 NinePatchChunk chunk = chunkRef.get();
197 if (chunk == null) {
198 ByteArrayInputStream bais = new ByteArrayInputStream(array);
199 ObjectInputStream ois = null;
200 try {
201 ois = new ObjectInputStream(bais);
202 chunk = (NinePatchChunk) ois.readObject();
203
204 // put back the chunk in the cache
205 if (chunk != null) {
206 sChunkCache.put(array, new SoftReference<NinePatchChunk>(chunk));
207 }
208 } catch (IOException e) {
Xavier Ducrohet3054fe62010-12-06 10:11:44 -0800209 Bridge.getLog().error("Failed to deserialize NinePatchChunk content.", e);
Xavier Ducrohete4d97872010-11-22 20:09:55 -0800210 return null;
211 } catch (ClassNotFoundException e) {
Xavier Ducrohet3054fe62010-12-06 10:11:44 -0800212 Bridge.getLog().error("Failed to deserialize NinePatchChunk class.", e);
Xavier Ducrohete4d97872010-11-22 20:09:55 -0800213 return null;
214 } finally {
215 if (ois != null) {
216 try {
217 ois.close();
218 } catch (IOException e) {
219 }
220 }
221 }
222 }
223
224 return chunk;
225 }
226}