OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / sdk / hierarchyviewer / src / com / android / hierarchyviewer / ui / util / PsdFile.java
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
17 package  com.android.hierarchyviewer.ui.util;
18
19 import java.awt.Graphics2D;
20 import java.awt.Point;
21 import java.awt.image.BufferedImage;
22 import java.io.BufferedOutputStream;
23 import java.io.DataOutputStream;
24 import java.io.IOException;
25 import java.io.OutputStream;
26 import java.io.UnsupportedEncodingException;
27 import java.util.ArrayList;
28 import java.util.List;
29
30 /**
31  * Writes PSD file.
32  * 
33  * Supports only 8 bits, RGB images with 4 channels.
34  */
35 public class PsdFile {
36     private final Header mHeader;
37     private final ColorMode mColorMode;
38     private final ImageResources mImageResources;
39     private final LayersMasksInfo mLayersMasksInfo;
40     private final LayersInfo mLayersInfo;
41
42     private final BufferedImage mMergedImage;
43     private final Graphics2D mGraphics;
44
45     public PsdFile(int width, int height) {
46         mHeader = new Header(width, height);
47         mColorMode = new ColorMode();
48         mImageResources = new ImageResources();
49         mLayersMasksInfo = new LayersMasksInfo();
50         mLayersInfo = new LayersInfo();
51
52         mMergedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
53         mGraphics = mMergedImage.createGraphics();
54     }
55
56     public void addLayer(String name, BufferedImage image, Point offset) {
57         addLayer(name, image, offset, true);
58     }
59     
60     public void addLayer(String name, BufferedImage image, Point offset, boolean visible) {
61         mLayersInfo.addLayer(name, image, offset, visible);
62         if (visible) mGraphics.drawImage(image, null, offset.x, offset.y);
63     }
64     
65     public void write(OutputStream stream) {
66         mLayersMasksInfo.setLayersInfo(mLayersInfo);
67
68         DataOutputStream out = new DataOutputStream(new BufferedOutputStream(stream));
69         try {
70             mHeader.write(out);
71             out.flush();
72
73             mColorMode.write(out);
74             mImageResources.write(out);
75             mLayersMasksInfo.write(out);
76             mLayersInfo.write(out);
77             out.flush();
78
79             mLayersInfo.writeImageData(out);
80             out.flush();
81             
82             writeImage(mMergedImage, out, false);
83             out.flush();
84         } catch (IOException e) {
85             e.printStackTrace();
86         } finally {
87             try {
88                 out.close();
89             } catch (IOException e) {
90                 e.printStackTrace();
91             }
92         }
93     }
94
95     private static void writeImage(BufferedImage image, DataOutputStream out, boolean split)
96             throws IOException {
97
98         if (!split) out.writeShort(0);
99         
100         int width = image.getWidth();
101         int height = image.getHeight();
102
103         final int length = width * height;
104         int[] pixels = new int[length];
105
106         image.getData().getDataElements(0, 0, width, height, pixels);
107
108         byte[] a = new byte[length];
109         byte[] r = new byte[length];
110         byte[] g = new byte[length];
111         byte[] b = new byte[length];
112
113         for (int i = 0; i < length; i++) {
114             final int pixel = pixels[i];
115             a[i] = (byte) ((pixel >> 24) & 0xFF);
116             r[i] = (byte) ((pixel >> 16) & 0xFF);
117             g[i] = (byte) ((pixel >>  8) & 0xFF);
118             b[i] = (byte)  (pixel        & 0xFF);
119         }
120
121         if (split) out.writeShort(0);
122         if (split) out.write(a);
123         if (split) out.writeShort(0);
124         out.write(r);
125         if (split) out.writeShort(0);
126         out.write(g);
127         if (split) out.writeShort(0);
128         out.write(b);
129         if (!split) out.write(a);
130     }
131
132     @SuppressWarnings({"UnusedDeclaration"})
133     static class Header {
134         static final short MODE_BITMAP = 0;
135         static final short MODE_GRAYSCALE = 1;
136         static final short MODE_INDEXED = 2;
137         static final short MODE_RGB = 3;
138         static final short MODE_CMYK = 4;
139         static final short MODE_MULTI_CHANNEL = 7;
140         static final short MODE_DUOTONE = 8;
141         static final short MODE_LAB = 9;
142
143         final byte[] mSignature = "8BPS".getBytes();
144         final short mVersion = 1;
145         final byte[] mReserved = new byte[6];
146         final short mChannelCount = 4;
147         final int mHeight;
148         final int mWidth;
149         final short mDepth = 8;
150         final short mMode = MODE_RGB;
151         
152         Header(int width, int height) {
153             mWidth = width;
154             mHeight = height;
155         }
156
157         void write(DataOutputStream out) throws IOException {
158             out.write(mSignature);
159             out.writeShort(mVersion);
160             out.write(mReserved);
161             out.writeShort(mChannelCount);
162             out.writeInt(mHeight);
163             out.writeInt(mWidth);
164             out.writeShort(mDepth);
165             out.writeShort(mMode);
166         }
167     }
168
169     // Unused at the moment
170     @SuppressWarnings({"UnusedDeclaration"})
171     static class ColorMode {
172         final int mLength = 0;
173         
174         void write(DataOutputStream out) throws IOException {
175             out.writeInt(mLength);
176         }
177     }
178     
179     // Unused at the moment
180     @SuppressWarnings({"UnusedDeclaration"})
181     static class ImageResources {
182         static final short RESOURCE_RESOLUTION_INFO = 0x03ED;
183         
184         int mLength = 0;
185
186         final byte[] mSignature = "8BIM".getBytes();
187         final short mResourceId = RESOURCE_RESOLUTION_INFO;
188         
189         final short mPad = 0;
190         
191         final int mDataLength = 16;
192
193         final short mHorizontalDisplayUnit = 0x48; // 72 dpi
194         final int mHorizontalResolution = 1;
195         final short mWidthDisplayUnit = 1;
196
197         final short mVerticalDisplayUnit = 0x48; // 72 dpi
198         final int mVerticalResolution = 1;
199         final short mHeightDisplayUnit = 1;
200         
201         ImageResources() {
202             mLength = mSignature.length;
203             mLength += 2;
204             mLength += 2;            
205             mLength += 4;            
206             mLength += 8;            
207             mLength += 8;            
208         }
209
210         void write(DataOutputStream out) throws IOException {
211             out.writeInt(mLength);
212             out.write(mSignature);
213             out.writeShort(mResourceId);
214             out.writeShort(mPad);
215             out.writeInt(mDataLength);
216             out.writeShort(mHorizontalDisplayUnit);
217             out.writeInt(mHorizontalResolution);
218             out.writeShort(mWidthDisplayUnit);
219             out.writeShort(mVerticalDisplayUnit);
220             out.writeInt(mVerticalResolution);
221             out.writeShort(mHeightDisplayUnit);
222         }
223     }
224     
225     @SuppressWarnings({"UnusedDeclaration"})
226     static class LayersMasksInfo {
227         int mMiscLength;
228         int mLayerInfoLength;
229         
230         void setLayersInfo(LayersInfo layersInfo) {
231             mLayerInfoLength = layersInfo.getLength();
232             // Round to the next multiple of 2
233             if ((mLayerInfoLength & 0x1) == 0x1) mLayerInfoLength++;
234             mMiscLength = mLayerInfoLength + 8;
235         }
236
237         void write(DataOutputStream out) throws IOException {
238             out.writeInt(mMiscLength);
239             out.writeInt(mLayerInfoLength);
240         }
241     }
242     
243     @SuppressWarnings({"UnusedDeclaration"})
244     static class LayersInfo {
245         final List<Layer> mLayers = new ArrayList<Layer>();
246
247         void addLayer(String name, BufferedImage image, Point offset, boolean visible) {
248             mLayers.add(new Layer(name, image, offset, visible));
249         }
250
251         int getLength() {
252             int length = 2;
253             for (Layer layer : mLayers) {
254                 length += layer.getLength();
255             }
256             return length;
257         }
258
259         void write(DataOutputStream out) throws IOException {
260             out.writeShort((short) -mLayers.size());
261             for (Layer layer : mLayers) {
262                 layer.write(out);
263             }
264         }
265
266         void writeImageData(DataOutputStream out) throws IOException {
267             for (Layer layer : mLayers) {
268                 layer.writeImageData(out);
269             }
270             // Global layer mask info length
271             out.writeInt(0);
272         }
273     }
274     
275     @SuppressWarnings({"UnusedDeclaration"})
276     static class Layer {
277         static final byte OPACITY_TRANSPARENT = 0x0;
278         static final byte OPACITY_OPAQUE = (byte) 0xFF;
279         
280         static final byte CLIPPING_BASE = 0x0;
281         static final byte CLIPPING_NON_BASE = 0x1;
282         
283         static final byte FLAG_TRANSPARENCY_PROTECTED = 0x1;
284         static final byte FLAG_INVISIBLE = 0x2;
285         
286         final int mTop;
287         final int mLeft;
288         final int mBottom;
289         final int mRight;
290
291         final short mChannelCount = 4;
292         final Channel[] mChannelInfo = new Channel[mChannelCount];
293
294         final byte[] mBlendSignature = "8BIM".getBytes();
295         final byte[] mBlendMode = "norm".getBytes();
296
297         final byte mOpacity = OPACITY_OPAQUE;
298         final byte mClipping = CLIPPING_BASE;
299         byte mFlags = 0x0;
300         final byte mFiller = 0x0;
301         
302         int mExtraSize = 4 + 4;
303
304         final int mMaskDataLength = 0;
305         final int mBlendRangeDataLength = 0;
306
307         final byte[] mName;
308
309         final byte[] mLayerExtraSignature = "8BIM".getBytes();
310         final byte[] mLayerExtraKey = "luni".getBytes();
311         int mLayerExtraLength;
312         final String mOriginalName;
313         
314         private BufferedImage mImage;
315
316         Layer(String name, BufferedImage image, Point offset, boolean visible) {
317             final int height = image.getHeight();
318             final int width = image.getWidth();
319             final int length = width * height;
320
321             mChannelInfo[0] = new Channel(Channel.ID_ALPHA, length);
322             mChannelInfo[1] = new Channel(Channel.ID_RED, length);
323             mChannelInfo[2] = new Channel(Channel.ID_GREEN, length);
324             mChannelInfo[3] = new Channel(Channel.ID_BLUE, length);
325
326             mTop = offset.y;
327             mLeft = offset.x;
328             mBottom = offset.y + height;
329             mRight = offset.x + width;
330
331             mOriginalName = name;
332             byte[] data = name.getBytes();
333
334             try {
335                 mLayerExtraLength = 4 + mOriginalName.getBytes("UTF-16").length;
336             } catch (UnsupportedEncodingException e) {
337                 e.printStackTrace();
338             }
339
340             final byte[] nameData = new byte[data.length + 1];
341             nameData[0] = (byte) (data.length & 0xFF);
342             System.arraycopy(data, 0, nameData, 1, data.length);
343
344             // This could be done in the same pass as above
345             if (nameData.length % 4 != 0) {
346                 data = new byte[nameData.length + 4 - (nameData.length % 4)];
347                 System.arraycopy(nameData, 0, data, 0, nameData.length);
348                 mName = data;
349             } else {
350                 mName = nameData;
351             }
352             mExtraSize += mName.length;
353             mExtraSize += mLayerExtraLength + 4 + mLayerExtraKey.length +
354                     mLayerExtraSignature.length;
355
356             mImage = image;
357             
358             if (!visible) {
359                 mFlags |= FLAG_INVISIBLE;
360             }
361         }
362
363         int getLength() {
364             int length = 4 * 4 + 2;
365
366             for (Channel channel : mChannelInfo) {
367                 length += channel.getLength();
368             }
369
370             length += mBlendSignature.length;
371             length += mBlendMode.length;
372             length += 4;
373             length += 4;
374             length += mExtraSize;
375
376             return length;
377         }
378
379         void write(DataOutputStream out) throws IOException {
380             out.writeInt(mTop);
381             out.writeInt(mLeft);
382             out.writeInt(mBottom);
383             out.writeInt(mRight);
384
385             out.writeShort(mChannelCount);
386             for (Channel channel : mChannelInfo) {
387                 channel.write(out);
388             }            
389
390             out.write(mBlendSignature);
391             out.write(mBlendMode);
392
393             out.write(mOpacity);
394             out.write(mClipping);
395             out.write(mFlags);
396             out.write(mFiller);
397
398             out.writeInt(mExtraSize);
399             out.writeInt(mMaskDataLength);
400
401             out.writeInt(mBlendRangeDataLength);            
402
403             out.write(mName);
404
405             out.write(mLayerExtraSignature);
406             out.write(mLayerExtraKey);
407             out.writeInt(mLayerExtraLength);
408             out.writeInt(mOriginalName.length() + 1);
409             out.write(mOriginalName.getBytes("UTF-16"));
410         }
411
412         void writeImageData(DataOutputStream out) throws IOException {
413             writeImage(mImage, out, true);
414         }
415     }
416     
417     @SuppressWarnings({"UnusedDeclaration"})
418     static class Channel {
419         static final short ID_RED = 0;
420         static final short ID_GREEN = 1;
421         static final short ID_BLUE = 2;
422         static final short ID_ALPHA = -1;
423         static final short ID_LAYER_MASK = -2;
424         
425         final short mId;
426         final int mDataLength;
427
428         Channel(short id, int dataLength) {
429             mId = id;
430             mDataLength = dataLength + 2;
431         }
432         
433         int getLength() {
434             return 2 + 4 + mDataLength;
435         }
436
437         void write(DataOutputStream out) throws IOException {
438             out.writeShort(mId);
439             out.writeInt(mDataLength);
440         }
441     }
442 }