aboutsummaryrefslogtreecommitdiffstats
path: root/hierarchyviewer
diff options
context:
space:
mode:
authorRomain Guy <romainguy@android.com>2010-03-02 17:07:03 -0800
committerRomain Guy <romainguy@android.com>2010-03-02 20:14:15 -0800
commit24651f91d4dd8904f6f3d28eba64a71955edc89e (patch)
treefcd5d91e541973508dcd88e2c316cd3113b2b042 /hierarchyviewer
parentbd6dfb3de102fe06357c9c8c5ee2b971348bd2dc (diff)
downloadsdk-24651f91d4dd8904f6f3d28eba64a71955edc89e.zip
sdk-24651f91d4dd8904f6f3d28eba64a71955edc89e.tar.gz
sdk-24651f91d4dd8904f6f3d28eba64a71955edc89e.tar.bz2
Add the ability to export any window as a layered PSD file.
Diffstat (limited to 'hierarchyviewer')
-rw-r--r--hierarchyviewer/src/com/android/hierarchyviewer/scene/CaptureLoader.java97
-rw-r--r--hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java44
-rw-r--r--hierarchyviewer/src/com/android/hierarchyviewer/ui/action/CaptureLayersAction.java42
-rw-r--r--hierarchyviewer/src/com/android/hierarchyviewer/ui/util/PsdFile.java442
-rw-r--r--hierarchyviewer/src/com/android/hierarchyviewer/ui/util/PsdFileFilter.java32
5 files changed, 656 insertions, 1 deletions
diff --git a/hierarchyviewer/src/com/android/hierarchyviewer/scene/CaptureLoader.java b/hierarchyviewer/src/com/android/hierarchyviewer/scene/CaptureLoader.java
index c512ac2..ca51b4e 100644
--- a/hierarchyviewer/src/com/android/hierarchyviewer/scene/CaptureLoader.java
+++ b/hierarchyviewer/src/com/android/hierarchyviewer/scene/CaptureLoader.java
@@ -17,13 +17,20 @@
package com.android.hierarchyviewer.scene;
import com.android.ddmlib.IDevice;
-import com.android.hierarchyviewer.device.Configuration;
import com.android.hierarchyviewer.device.Window;
import com.android.hierarchyviewer.device.DeviceBridge;
+import com.android.hierarchyviewer.ui.util.PsdFile;
+import java.awt.Graphics2D;
import java.awt.Image;
+import java.awt.Point;
+import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
@@ -31,6 +38,94 @@ import java.net.Socket;
import javax.imageio.ImageIO;
public class CaptureLoader {
+ public static boolean saveLayers(IDevice device, Window window, File file) {
+ Socket socket = null;
+ DataInputStream in = null;
+ BufferedWriter out = null;
+ boolean result = false;
+
+ try {
+ socket = new Socket();
+ socket.connect(new InetSocketAddress("127.0.0.1",
+ DeviceBridge.getDeviceLocalPort(device)));
+
+ out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
+ in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
+
+ out.write("CAPTURE_LAYERS " + window.encode());
+ out.newLine();
+ out.flush();
+
+ int width = in.readInt();
+ int height = in.readInt();
+
+ PsdFile psd = new PsdFile(width, height);
+
+ while (readLayer(in, psd)) {
+ }
+
+ psd.write(new FileOutputStream(file));
+
+ result = true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (out != null) {
+ out.close();
+ }
+ if (in != null) {
+ in.close();
+ }
+ if (socket != null) {
+ socket.close();
+ }
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ return result;
+ }
+
+ private static boolean readLayer(DataInputStream in, PsdFile psd) {
+ try {
+ if (in.read() == 2) {
+ System.out.println("Found end of layers list");
+ return false;
+ }
+ String name = in.readUTF();
+ System.out.println("name = " + name);
+ boolean visible = in.read() == 1;
+ int x = in.readInt();
+ int y = in.readInt();
+ int dataSize = in.readInt();
+
+ byte[] data = new byte[dataSize];
+ int read = 0;
+ while (read < dataSize) {
+ read += in.read(data, read, dataSize - read);
+ }
+
+ ByteArrayInputStream arrayIn = new ByteArrayInputStream(data);
+ BufferedImage chunk = ImageIO.read(arrayIn);
+
+ // Ensure the image is in the right format
+ BufferedImage image = new BufferedImage(chunk.getWidth(), chunk.getHeight(),
+ BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = image.createGraphics();
+ g.drawImage(chunk, null, 0, 0);
+ g.dispose();
+
+ psd.addLayer(name, image, new Point(x, y), visible);
+
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
public static Image loadCapture(IDevice device, Window window, String params) {
Socket socket = null;
BufferedInputStream in = null;
diff --git a/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java b/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java
index 5686496..a7db985 100644
--- a/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java
+++ b/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java
@@ -29,12 +29,14 @@ import com.android.hierarchyviewer.scene.ViewManager;
import com.android.hierarchyviewer.scene.ViewNode;
import com.android.hierarchyviewer.scene.WindowsLoader;
import com.android.hierarchyviewer.scene.ProfilesLoader;
+import com.android.hierarchyviewer.ui.util.PsdFileFilter;
import com.android.hierarchyviewer.util.OS;
import com.android.hierarchyviewer.util.WorkerThread;
import com.android.hierarchyviewer.ui.action.ShowDevicesAction;
import com.android.hierarchyviewer.ui.action.RequestLayoutAction;
import com.android.hierarchyviewer.ui.action.InvalidateAction;
import com.android.hierarchyviewer.ui.action.CaptureNodeAction;
+import com.android.hierarchyviewer.ui.action.CaptureLayersAction;
import com.android.hierarchyviewer.ui.action.RefreshWindowsAction;
import com.android.hierarchyviewer.ui.action.StopServerAction;
import com.android.hierarchyviewer.ui.action.StartServerAction;
@@ -152,6 +154,7 @@ public class Workspace extends JFrame {
private Window currentWindow = Window.FOCUSED_WINDOW;
private JButton displayNodeButton;
+ private JButton captureLayersButton;
private JButton invalidateButton;
private JButton requestLayoutButton;
private JButton loadButton;
@@ -200,6 +203,7 @@ public class Workspace extends JFrame {
actionsMap.put(InvalidateAction.ACTION_NAME, new InvalidateAction(this));
actionsMap.put(RequestLayoutAction.ACTION_NAME, new RequestLayoutAction(this));
actionsMap.put(CaptureNodeAction.ACTION_NAME, new CaptureNodeAction(this));
+ actionsMap.put(CaptureLayersAction.ACTION_NAME, new CaptureLayersAction(this));
actionsMap.put(RefreshWindowsAction.ACTION_NAME, new RefreshWindowsAction(this));
}
@@ -477,6 +481,12 @@ public class Workspace extends JFrame {
displayNodeButton.putClientProperty("JButton.segmentPosition", "first");
toolBar.add(displayNodeButton);
+ captureLayersButton = new JButton();
+ captureLayersButton.setAction(actionsMap.get(CaptureLayersAction.ACTION_NAME));
+ captureLayersButton.putClientProperty("JButton.buttonType", "segmentedTextured");
+ captureLayersButton.putClientProperty("JButton.segmentPosition", "middle");
+ toolBar.add(captureLayersButton);
+
invalidateButton = new JButton();
invalidateButton.setAction(actionsMap.get(InvalidateAction.ACTION_NAME));
invalidateButton.putClientProperty("JButton.buttonType", "segmentedTextured");
@@ -707,6 +717,7 @@ public class Workspace extends JFrame {
mainSplitter.setDividerLocation(getWidth() - mainSplitter.getDividerSize() -
buttonsPanel.getPreferredSize().width);
+ captureLayersButton.setEnabled(true);
saveMenuItem.setEnabled(true);
showPixelPerfectTree();
@@ -872,6 +883,7 @@ public class Workspace extends JFrame {
showDevicesMenuItem.setEnabled(false);
showDevicesButton.setEnabled(false);
displayNodeButton.setEnabled(false);
+ captureLayersButton.setEnabled(false);
invalidateButton.setEnabled(false);
requestLayoutButton.setEnabled(false);
graphViewButton.setEnabled(false);
@@ -900,6 +912,7 @@ public class Workspace extends JFrame {
saveMenuItem.setEnabled(false);
loadButton.setEnabled(false);
displayNodeButton.setEnabled(false);
+ captureLayersButton.setEnabled(false);
invalidateButton.setEnabled(false);
graphViewButton.setEnabled(false);
pixelPerfectViewButton.setEnabled(false);
@@ -994,6 +1007,17 @@ public class Workspace extends JFrame {
}
return new CaptureNodeTask();
}
+
+ public SwingWorker<?, ?> captureLayers() {
+ JFileChooser chooser = new JFileChooser();
+ chooser.setFileFilter(new PsdFileFilter());
+ int choice = chooser.showSaveDialog(sceneView);
+ if (choice == JFileChooser.APPROVE_OPTION) {
+ return new CaptureLayersTask(chooser.getSelectedFile());
+ } else {
+ return null;
+ }
+ }
public SwingWorker<?, ?> startServer() {
return new StartServerTask();
@@ -1077,6 +1101,26 @@ public class Workspace extends JFrame {
endTask();
}
}
+
+ private class CaptureLayersTask extends SwingWorker<Boolean, Void> {
+ private File file;
+
+ private CaptureLayersTask(File file) {
+ this.file = file;
+ beginTask();
+ }
+
+ @Override
+ @WorkerThread
+ protected Boolean doInBackground() throws Exception {
+ return CaptureLoader.saveLayers(currentDevice, currentWindow, file);
+ }
+
+ @Override
+ protected void done() {
+ endTask();
+ }
+ }
private class CaptureNodeTask extends SwingWorker<Image, Void> {
private String captureParams;
diff --git a/hierarchyviewer/src/com/android/hierarchyviewer/ui/action/CaptureLayersAction.java b/hierarchyviewer/src/com/android/hierarchyviewer/ui/action/CaptureLayersAction.java
new file mode 100644
index 0000000..2fff041
--- /dev/null
+++ b/hierarchyviewer/src/com/android/hierarchyviewer/ui/action/CaptureLayersAction.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hierarchyviewer.ui.action;
+
+import com.android.hierarchyviewer.ui.Workspace;
+
+import javax.swing.KeyStroke;
+import java.awt.event.KeyEvent;
+import java.awt.event.ActionEvent;
+import java.awt.Toolkit;
+
+public class CaptureLayersAction extends BackgroundAction {
+ public static final String ACTION_NAME = "captureLayers";
+ private Workspace mWorkspace;
+
+ public CaptureLayersAction(Workspace workspace) {
+ putValue(NAME, "Capture PSD");
+ putValue(SHORT_DESCRIPTION, "Capture PSD");
+ putValue(LONG_DESCRIPTION, "Capture current window into a Photoshop PSD file");
+ putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_P,
+ Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
+ this.mWorkspace = workspace;
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ executeBackgroundTask(mWorkspace.captureLayers());
+ }
+}
diff --git a/hierarchyviewer/src/com/android/hierarchyviewer/ui/util/PsdFile.java b/hierarchyviewer/src/com/android/hierarchyviewer/ui/util/PsdFile.java
new file mode 100644
index 0000000..3768e41
--- /dev/null
+++ b/hierarchyviewer/src/com/android/hierarchyviewer/ui/util/PsdFile.java
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hierarchyviewer.ui.util;
+
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.image.BufferedImage;
+import java.io.BufferedOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Writes PSD file.
+ *
+ * Supports only 8 bits, RGB images with 4 channels.
+ */
+public class PsdFile {
+ private final Header mHeader;
+ private final ColorMode mColorMode;
+ private final ImageResources mImageResources;
+ private final LayersMasksInfo mLayersMasksInfo;
+ private final LayersInfo mLayersInfo;
+
+ private final BufferedImage mMergedImage;
+ private final Graphics2D mGraphics;
+
+ public PsdFile(int width, int height) {
+ mHeader = new Header(width, height);
+ mColorMode = new ColorMode();
+ mImageResources = new ImageResources();
+ mLayersMasksInfo = new LayersMasksInfo();
+ mLayersInfo = new LayersInfo();
+
+ mMergedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ mGraphics = mMergedImage.createGraphics();
+ }
+
+ public void addLayer(String name, BufferedImage image, Point offset) {
+ addLayer(name, image, offset, true);
+ }
+
+ public void addLayer(String name, BufferedImage image, Point offset, boolean visible) {
+ mLayersInfo.addLayer(name, image, offset, visible);
+ if (visible) mGraphics.drawImage(image, null, offset.x, offset.y);
+ }
+
+ public void write(OutputStream stream) {
+ mLayersMasksInfo.setLayersInfo(mLayersInfo);
+
+ DataOutputStream out = new DataOutputStream(new BufferedOutputStream(stream));
+ try {
+ mHeader.write(out);
+ out.flush();
+
+ mColorMode.write(out);
+ mImageResources.write(out);
+ mLayersMasksInfo.write(out);
+ mLayersInfo.write(out);
+ out.flush();
+
+ mLayersInfo.writeImageData(out);
+ out.flush();
+
+ writeImage(mMergedImage, out, false);
+ out.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ out.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private static void writeImage(BufferedImage image, DataOutputStream out, boolean split)
+ throws IOException {
+
+ if (!split) out.writeShort(0);
+
+ int width = image.getWidth();
+ int height = image.getHeight();
+
+ final int length = width * height;
+ int[] pixels = new int[length];
+
+ image.getData().getDataElements(0, 0, width, height, pixels);
+
+ byte[] a = new byte[length];
+ byte[] r = new byte[length];
+ byte[] g = new byte[length];
+ byte[] b = new byte[length];
+
+ for (int i = 0; i < length; i++) {
+ final int pixel = pixels[i];
+ a[i] = (byte) ((pixel >> 24) & 0xFF);
+ r[i] = (byte) ((pixel >> 16) & 0xFF);
+ g[i] = (byte) ((pixel >> 8) & 0xFF);
+ b[i] = (byte) (pixel & 0xFF);
+ }
+
+ if (split) out.writeShort(0);
+ if (split) out.write(a);
+ if (split) out.writeShort(0);
+ out.write(r);
+ if (split) out.writeShort(0);
+ out.write(g);
+ if (split) out.writeShort(0);
+ out.write(b);
+ if (!split) out.write(a);
+ }
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ static class Header {
+ static final short MODE_BITMAP = 0;
+ static final short MODE_GRAYSCALE = 1;
+ static final short MODE_INDEXED = 2;
+ static final short MODE_RGB = 3;
+ static final short MODE_CMYK = 4;
+ static final short MODE_MULTI_CHANNEL = 7;
+ static final short MODE_DUOTONE = 8;
+ static final short MODE_LAB = 9;
+
+ final byte[] mSignature = "8BPS".getBytes();
+ final short mVersion = 1;
+ final byte[] mReserved = new byte[6];
+ final short mChannelCount = 4;
+ final int mHeight;
+ final int mWidth;
+ final short mDepth = 8;
+ final short mMode = MODE_RGB;
+
+ Header(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ }
+
+ void write(DataOutputStream out) throws IOException {
+ out.write(mSignature);
+ out.writeShort(mVersion);
+ out.write(mReserved);
+ out.writeShort(mChannelCount);
+ out.writeInt(mHeight);
+ out.writeInt(mWidth);
+ out.writeShort(mDepth);
+ out.writeShort(mMode);
+ }
+ }
+
+ // Unused at the moment
+ @SuppressWarnings({"UnusedDeclaration"})
+ static class ColorMode {
+ final int mLength = 0;
+
+ void write(DataOutputStream out) throws IOException {
+ out.writeInt(mLength);
+ }
+ }
+
+ // Unused at the moment
+ @SuppressWarnings({"UnusedDeclaration"})
+ static class ImageResources {
+ static final short RESOURCE_RESOLUTION_INFO = 0x03ED;
+
+ int mLength = 0;
+
+ final byte[] mSignature = "8BIM".getBytes();
+ final short mResourceId = RESOURCE_RESOLUTION_INFO;
+
+ final short mPad = 0;
+
+ final int mDataLength = 16;
+
+ final short mHorizontalDisplayUnit = 0x48; // 72 dpi
+ final int mHorizontalResolution = 1;
+ final short mWidthDisplayUnit = 1;
+
+ final short mVerticalDisplayUnit = 0x48; // 72 dpi
+ final int mVerticalResolution = 1;
+ final short mHeightDisplayUnit = 1;
+
+ ImageResources() {
+ mLength = mSignature.length;
+ mLength += 2;
+ mLength += 2;
+ mLength += 4;
+ mLength += 8;
+ mLength += 8;
+ }
+
+ void write(DataOutputStream out) throws IOException {
+ out.writeInt(mLength);
+ out.write(mSignature);
+ out.writeShort(mResourceId);
+ out.writeShort(mPad);
+ out.writeInt(mDataLength);
+ out.writeShort(mHorizontalDisplayUnit);
+ out.writeInt(mHorizontalResolution);
+ out.writeShort(mWidthDisplayUnit);
+ out.writeShort(mVerticalDisplayUnit);
+ out.writeInt(mVerticalResolution);
+ out.writeShort(mHeightDisplayUnit);
+ }
+ }
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ static class LayersMasksInfo {
+ int mMiscLength;
+ int mLayerInfoLength;
+
+ void setLayersInfo(LayersInfo layersInfo) {
+ mLayerInfoLength = layersInfo.getLength();
+ // Round to the next multiple of 2
+ if ((mLayerInfoLength & 0x1) == 0x1) mLayerInfoLength++;
+ mMiscLength = mLayerInfoLength + 8;
+ }
+
+ void write(DataOutputStream out) throws IOException {
+ out.writeInt(mMiscLength);
+ out.writeInt(mLayerInfoLength);
+ }
+ }
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ static class LayersInfo {
+ final List<Layer> mLayers = new ArrayList<Layer>();
+
+ void addLayer(String name, BufferedImage image, Point offset, boolean visible) {
+ mLayers.add(new Layer(name, image, offset, visible));
+ }
+
+ int getLength() {
+ int length = 2;
+ for (Layer layer : mLayers) {
+ length += layer.getLength();
+ }
+ return length;
+ }
+
+ void write(DataOutputStream out) throws IOException {
+ out.writeShort((short) -mLayers.size());
+ for (Layer layer : mLayers) {
+ layer.write(out);
+ }
+ }
+
+ void writeImageData(DataOutputStream out) throws IOException {
+ for (Layer layer : mLayers) {
+ layer.writeImageData(out);
+ }
+ // Global layer mask info length
+ out.writeInt(0);
+ }
+ }
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ static class Layer {
+ static final byte OPACITY_TRANSPARENT = 0x0;
+ static final byte OPACITY_OPAQUE = (byte) 0xFF;
+
+ static final byte CLIPPING_BASE = 0x0;
+ static final byte CLIPPING_NON_BASE = 0x1;
+
+ static final byte FLAG_TRANSPARENCY_PROTECTED = 0x1;
+ static final byte FLAG_INVISIBLE = 0x2;
+
+ final int mTop;
+ final int mLeft;
+ final int mBottom;
+ final int mRight;
+
+ final short mChannelCount = 4;
+ final Channel[] mChannelInfo = new Channel[mChannelCount];
+
+ final byte[] mBlendSignature = "8BIM".getBytes();
+ final byte[] mBlendMode = "norm".getBytes();
+
+ final byte mOpacity = OPACITY_OPAQUE;
+ final byte mClipping = CLIPPING_BASE;
+ byte mFlags = 0x0;
+ final byte mFiller = 0x0;
+
+ int mExtraSize = 4 + 4;
+
+ final int mMaskDataLength = 0;
+ final int mBlendRangeDataLength = 0;
+
+ final byte[] mName;
+
+ final byte[] mLayerExtraSignature = "8BIM".getBytes();
+ final byte[] mLayerExtraKey = "luni".getBytes();
+ int mLayerExtraLength;
+ final String mOriginalName;
+
+ private BufferedImage mImage;
+
+ Layer(String name, BufferedImage image, Point offset, boolean visible) {
+ final int height = image.getHeight();
+ final int width = image.getWidth();
+ final int length = width * height;
+
+ mChannelInfo[0] = new Channel(Channel.ID_ALPHA, length);
+ mChannelInfo[1] = new Channel(Channel.ID_RED, length);
+ mChannelInfo[2] = new Channel(Channel.ID_GREEN, length);
+ mChannelInfo[3] = new Channel(Channel.ID_BLUE, length);
+
+ mTop = offset.y;
+ mLeft = offset.x;
+ mBottom = offset.y + height;
+ mRight = offset.x + width;
+
+ mOriginalName = name;
+ byte[] data = name.getBytes();
+
+ try {
+ mLayerExtraLength = 4 + mOriginalName.getBytes("UTF-16").length;
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+
+ final byte[] nameData = new byte[data.length + 1];
+ nameData[0] = (byte) (data.length & 0xFF);
+ System.arraycopy(data, 0, nameData, 1, data.length);
+
+ // This could be done in the same pass as above
+ if (nameData.length % 4 != 0) {
+ data = new byte[nameData.length + 4 - (nameData.length % 4)];
+ System.arraycopy(nameData, 0, data, 0, nameData.length);
+ mName = data;
+ } else {
+ mName = nameData;
+ }
+ mExtraSize += mName.length;
+ mExtraSize += mLayerExtraLength + 4 + mLayerExtraKey.length +
+ mLayerExtraSignature.length;
+
+ mImage = image;
+
+ if (!visible) {
+ mFlags |= FLAG_INVISIBLE;
+ }
+ }
+
+ int getLength() {
+ int length = 4 * 4 + 2;
+
+ for (Channel channel : mChannelInfo) {
+ length += channel.getLength();
+ }
+
+ length += mBlendSignature.length;
+ length += mBlendMode.length;
+ length += 4;
+ length += 4;
+ length += mExtraSize;
+
+ return length;
+ }
+
+ void write(DataOutputStream out) throws IOException {
+ out.writeInt(mTop);
+ out.writeInt(mLeft);
+ out.writeInt(mBottom);
+ out.writeInt(mRight);
+
+ out.writeShort(mChannelCount);
+ for (Channel channel : mChannelInfo) {
+ channel.write(out);
+ }
+
+ out.write(mBlendSignature);
+ out.write(mBlendMode);
+
+ out.write(mOpacity);
+ out.write(mClipping);
+ out.write(mFlags);
+ out.write(mFiller);
+
+ out.writeInt(mExtraSize);
+ out.writeInt(mMaskDataLength);
+
+ out.writeInt(mBlendRangeDataLength);
+
+ out.write(mName);
+
+ out.write(mLayerExtraSignature);
+ out.write(mLayerExtraKey);
+ out.writeInt(mLayerExtraLength);
+ out.writeInt(mOriginalName.length() + 1);
+ out.write(mOriginalName.getBytes("UTF-16"));
+ }
+
+ void writeImageData(DataOutputStream out) throws IOException {
+ writeImage(mImage, out, true);
+ }
+ }
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ static class Channel {
+ static final short ID_RED = 0;
+ static final short ID_GREEN = 1;
+ static final short ID_BLUE = 2;
+ static final short ID_ALPHA = -1;
+ static final short ID_LAYER_MASK = -2;
+
+ final short mId;
+ final int mDataLength;
+
+ Channel(short id, int dataLength) {
+ mId = id;
+ mDataLength = dataLength + 2;
+ }
+
+ int getLength() {
+ return 2 + 4 + mDataLength;
+ }
+
+ void write(DataOutputStream out) throws IOException {
+ out.writeShort(mId);
+ out.writeInt(mDataLength);
+ }
+ }
+}
diff --git a/hierarchyviewer/src/com/android/hierarchyviewer/ui/util/PsdFileFilter.java b/hierarchyviewer/src/com/android/hierarchyviewer/ui/util/PsdFileFilter.java
new file mode 100644
index 0000000..6a7ce5b
--- /dev/null
+++ b/hierarchyviewer/src/com/android/hierarchyviewer/ui/util/PsdFileFilter.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hierarchyviewer.ui.util;
+
+import javax.swing.filechooser.FileFilter;
+import java.io.File;
+
+public class PsdFileFilter extends FileFilter {
+ @Override
+ public boolean accept(File f) {
+ return f.isDirectory() || f.getName().toLowerCase().endsWith(".psd");
+ }
+
+ @Override
+ public String getDescription() {
+ return "Photoshop Document (*.psd)";
+ }
+} \ No newline at end of file