From 4af9f005b1ee9c63d82834215936b890282ad017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Wi=C5=9Bniewski?= Date: Sat, 12 Jun 2010 16:06:18 +0200 Subject: [PATCH] Added 9patch decoding. --- src/brut/androlib/res/AndrolibResources.java | 6 +- .../res/decoder/Res9patchStreamDecoder.java | 139 ++++++++++++++++++ .../androlib/res/decoder/ResFileDecoder.java | 14 +- 3 files changed, 147 insertions(+), 12 deletions(-) create mode 100644 src/brut/androlib/res/decoder/Res9patchStreamDecoder.java diff --git a/src/brut/androlib/res/AndrolibResources.java b/src/brut/androlib/res/AndrolibResources.java index fbbc2ef1..e6d05a1c 100644 --- a/src/brut/androlib/res/AndrolibResources.java +++ b/src/brut/androlib/res/AndrolibResources.java @@ -104,7 +104,7 @@ final public class AndrolibResources { attrDecoder.setCurrentPackage( resTable.listMainPackages().iterator().next()); - Directory in, out, out9Patch; + Directory in, out; try { in = apkFile.getDirectory(); out = new FileDirectory(outDir); @@ -112,7 +112,6 @@ final public class AndrolibResources { fileDecoder.decode( in, "AndroidManifest.xml", out, "AndroidManifest.xml", "xml"); - out9Patch = out.createDir("9patch/res"); in = in.getDir("res"); out = out.createDir("res"); } catch (DirectoryException ex) { @@ -123,7 +122,7 @@ final public class AndrolibResources { for (ResPackage pkg : resTable.listMainPackages()) { attrDecoder.setCurrentPackage(pkg); for (ResResource res : pkg.listFiles()) { - fileDecoder.decode(res, in, out, out9Patch); + fileDecoder.decode(res, in, out); } for (ResValuesFile valuesFile : pkg.listValuesFiles()) { generateValuesFile(valuesFile, out, xmlSerializer); @@ -213,6 +212,7 @@ final public class AndrolibResources { ResStreamDecoderContainer decoders = new ResStreamDecoderContainer(); decoders.setDecoder("raw", new ResRawStreamDecoder()); + decoders.setDecoder("9patch", new Res9patchStreamDecoder()); ResAttrDecoder attrDecoder = new ResAttrDecoder(); AXmlResourceParser axmlParser = new AXmlResourceParser(); diff --git a/src/brut/androlib/res/decoder/Res9patchStreamDecoder.java b/src/brut/androlib/res/decoder/Res9patchStreamDecoder.java new file mode 100644 index 00000000..cadba789 --- /dev/null +++ b/src/brut/androlib/res/decoder/Res9patchStreamDecoder.java @@ -0,0 +1,139 @@ +/* + * Copyright 2010 Ryszard Wiśniewski . + * + * 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. + * under the License. + */ + +package brut.androlib.res.decoder; + +import brut.androlib.AndrolibException; +import brut.util.ExtDataInput; +import java.awt.image.BufferedImage; +import java.io.*; +import javax.imageio.ImageIO; +import org.apache.commons.io.IOUtils; + +/** + * @author Ryszard Wiśniewski + */ +public class Res9patchStreamDecoder implements ResStreamDecoder { + public void decode(InputStream in, OutputStream out) + throws AndrolibException { + try { + byte[] data = IOUtils.toByteArray(in); + + BufferedImage im = ImageIO.read(new ByteArrayInputStream(data)); + int w = im.getWidth(), h = im.getHeight(); + + BufferedImage im2 = new BufferedImage( + w + 2, h + 2, BufferedImage.TYPE_4BYTE_ABGR); + if (im.getType() == BufferedImage.TYPE_4BYTE_ABGR) { + im2.getRaster().setRect(1, 1, im.getRaster()); + } else { + im2.getGraphics().drawImage(im, 1, 1, null); + } + + NinePatch np = getNinePatch(data); + drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight); + drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom); + + int[] xDivs = np.xDivs; + for (int i = 0; i < xDivs.length; i += 2) { + drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]); + } + + int[] yDivs = np.yDivs; + for (int i = 0; i < yDivs.length; i += 2) { + drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]); + } + + ImageIO.write(im2, "png", out); + } catch (IOException ex) { + throw new AndrolibException(ex); + } + } + + private NinePatch getNinePatch(byte[] data) + throws AndrolibException, IOException { + ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data)); + find9patchChunk(di); + return NinePatch.decode(di); + } + + private void find9patchChunk(DataInput di) + throws AndrolibException, IOException { + di.skipBytes(8); + while (true) { + int size; + try { + size = di.readInt(); + } catch (IOException ex) { + throw new AndrolibException("Cant find nine patch chunk", ex); + } + if (di.readInt() == NP_CHUNK_TYPE) { + return; + } + di.skipBytes(size + 4); + } + } + + private void drawHLine(BufferedImage im, int y, int x1, int x2) { + for (int x = x1; x <= x2; x++) { + im.setRGB(x, y, NP_COLOR); + } + } + + private void drawVLine(BufferedImage im, int x, int y1, int y2) { + for (int y = y1; y <= y2; y++) { + im.setRGB(x, y, NP_COLOR); + } + } + + private static final int NP_CHUNK_TYPE = 0x6e705463; + private static final int NP_COLOR = 0xff000000; + + + private static class NinePatch { + public final int padLeft, padRight, padTop, padBottom; + public final int[] xDivs, yDivs; + + public NinePatch(int padLeft, int padRight, int padTop, int padBottom, + int[] xDivs, int[] yDivs) { + this.padLeft = padLeft; + this.padRight = padRight; + this.padTop = padTop; + this.padBottom = padBottom; + this.xDivs = xDivs; + this.yDivs = yDivs; + } + + public static NinePatch decode(ExtDataInput di) throws IOException { + di.skipBytes(1); + byte numXDivs = di.readByte(); + byte numYDivs = di.readByte(); + di.skipBytes(1); + di.skipBytes(8); + int padLeft = di.readInt(); + int padRight = di.readInt(); + int padTop = di.readInt(); + int padBottom = di.readInt(); + di.skipBytes(4); + int[] xDivs = di.readIntArray(numXDivs); + int[] yDivs = di.readIntArray(numYDivs); + + return new NinePatch(padLeft, padRight, padTop, padBottom, + xDivs, yDivs); + } + } +} diff --git a/src/brut/androlib/res/decoder/ResFileDecoder.java b/src/brut/androlib/res/decoder/ResFileDecoder.java index 3a6d31ca..d43c4d84 100644 --- a/src/brut/androlib/res/decoder/ResFileDecoder.java +++ b/src/brut/androlib/res/decoder/ResFileDecoder.java @@ -22,10 +22,7 @@ import brut.androlib.res.data.ResResource; import brut.androlib.res.data.value.ResFileValue; import brut.directory.Directory; import brut.directory.DirectoryException; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; import java.util.logging.Level; import java.util.logging.Logger; @@ -39,8 +36,8 @@ public class ResFileDecoder { this.mDecoders = decoders; } - public void decode(ResResource res, Directory inDir, Directory outDir, - Directory out9Patch) throws AndrolibException { + public void decode(ResResource res, Directory inDir, Directory outDir) + throws AndrolibException { ResFileValue fileValue = (ResFileValue) res.getValue(); String inFileName = fileValue.getStrippedPath(); @@ -63,9 +60,8 @@ public class ResFileDecoder { } if (typeName.equals("drawable")) { if (inFileName.toLowerCase().endsWith(".9.png")) { - outFileName = outResName + ".png"; - decode(inDir, inFileName, outDir, outFileName, "raw"); - decode(inDir, inFileName, out9Patch, outFileName, "raw"); + outFileName = outResName + ".9" + ext; + decode(inDir, inFileName, outDir, outFileName, "9patch"); return; } if (! ext.equals(".xml")) {