NinePatch_Delegate.java revision 90c6b7e639921e264ac65699439578bcbdbf583a
1/*
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
19import com.android.layoutlib.bridge.Bridge;
20import 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) {
75            Bridge.getLog().error("Failed to serialize NinePatchChunk.", e);
76            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, destDensity, srcDensity);
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) {
209                Bridge.getLog().error("Failed to deserialize NinePatchChunk content.", e);
210                return null;
211            } catch (ClassNotFoundException e) {
212                Bridge.getLog().error("Failed to deserialize NinePatchChunk class.", e);
213                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}
227