mirror of
https://github.com/revanced/Apktool.git
synced 2025-02-10 02:46:48 +01:00
Added 9patch decoding.
This commit is contained in:
parent
710d7b54d7
commit
4af9f005b1
@ -104,7 +104,7 @@ final public class AndrolibResources {
|
|||||||
attrDecoder.setCurrentPackage(
|
attrDecoder.setCurrentPackage(
|
||||||
resTable.listMainPackages().iterator().next());
|
resTable.listMainPackages().iterator().next());
|
||||||
|
|
||||||
Directory in, out, out9Patch;
|
Directory in, out;
|
||||||
try {
|
try {
|
||||||
in = apkFile.getDirectory();
|
in = apkFile.getDirectory();
|
||||||
out = new FileDirectory(outDir);
|
out = new FileDirectory(outDir);
|
||||||
@ -112,7 +112,6 @@ final public class AndrolibResources {
|
|||||||
fileDecoder.decode(
|
fileDecoder.decode(
|
||||||
in, "AndroidManifest.xml", out, "AndroidManifest.xml", "xml");
|
in, "AndroidManifest.xml", out, "AndroidManifest.xml", "xml");
|
||||||
|
|
||||||
out9Patch = out.createDir("9patch/res");
|
|
||||||
in = in.getDir("res");
|
in = in.getDir("res");
|
||||||
out = out.createDir("res");
|
out = out.createDir("res");
|
||||||
} catch (DirectoryException ex) {
|
} catch (DirectoryException ex) {
|
||||||
@ -123,7 +122,7 @@ final public class AndrolibResources {
|
|||||||
for (ResPackage pkg : resTable.listMainPackages()) {
|
for (ResPackage pkg : resTable.listMainPackages()) {
|
||||||
attrDecoder.setCurrentPackage(pkg);
|
attrDecoder.setCurrentPackage(pkg);
|
||||||
for (ResResource res : pkg.listFiles()) {
|
for (ResResource res : pkg.listFiles()) {
|
||||||
fileDecoder.decode(res, in, out, out9Patch);
|
fileDecoder.decode(res, in, out);
|
||||||
}
|
}
|
||||||
for (ResValuesFile valuesFile : pkg.listValuesFiles()) {
|
for (ResValuesFile valuesFile : pkg.listValuesFiles()) {
|
||||||
generateValuesFile(valuesFile, out, xmlSerializer);
|
generateValuesFile(valuesFile, out, xmlSerializer);
|
||||||
@ -213,6 +212,7 @@ final public class AndrolibResources {
|
|||||||
ResStreamDecoderContainer decoders =
|
ResStreamDecoderContainer decoders =
|
||||||
new ResStreamDecoderContainer();
|
new ResStreamDecoderContainer();
|
||||||
decoders.setDecoder("raw", new ResRawStreamDecoder());
|
decoders.setDecoder("raw", new ResRawStreamDecoder());
|
||||||
|
decoders.setDecoder("9patch", new Res9patchStreamDecoder());
|
||||||
|
|
||||||
ResAttrDecoder attrDecoder = new ResAttrDecoder();
|
ResAttrDecoder attrDecoder = new ResAttrDecoder();
|
||||||
AXmlResourceParser axmlParser = new AXmlResourceParser();
|
AXmlResourceParser axmlParser = new AXmlResourceParser();
|
||||||
|
139
src/brut/androlib/res/decoder/Res9patchStreamDecoder.java
Normal file
139
src/brut/androlib/res/decoder/Res9patchStreamDecoder.java
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
|
||||||
|
*
|
||||||
|
* 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 <brut.alll@gmail.com>
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,10 +22,7 @@ import brut.androlib.res.data.ResResource;
|
|||||||
import brut.androlib.res.data.value.ResFileValue;
|
import brut.androlib.res.data.value.ResFileValue;
|
||||||
import brut.directory.Directory;
|
import brut.directory.Directory;
|
||||||
import brut.directory.DirectoryException;
|
import brut.directory.DirectoryException;
|
||||||
import java.io.File;
|
import java.io.*;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@ -39,8 +36,8 @@ public class ResFileDecoder {
|
|||||||
this.mDecoders = decoders;
|
this.mDecoders = decoders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decode(ResResource res, Directory inDir, Directory outDir,
|
public void decode(ResResource res, Directory inDir, Directory outDir)
|
||||||
Directory out9Patch) throws AndrolibException {
|
throws AndrolibException {
|
||||||
|
|
||||||
ResFileValue fileValue = (ResFileValue) res.getValue();
|
ResFileValue fileValue = (ResFileValue) res.getValue();
|
||||||
String inFileName = fileValue.getStrippedPath();
|
String inFileName = fileValue.getStrippedPath();
|
||||||
@ -63,9 +60,8 @@ public class ResFileDecoder {
|
|||||||
}
|
}
|
||||||
if (typeName.equals("drawable")) {
|
if (typeName.equals("drawable")) {
|
||||||
if (inFileName.toLowerCase().endsWith(".9.png")) {
|
if (inFileName.toLowerCase().endsWith(".9.png")) {
|
||||||
outFileName = outResName + ".png";
|
outFileName = outResName + ".9" + ext;
|
||||||
decode(inDir, inFileName, outDir, outFileName, "raw");
|
decode(inDir, inFileName, outDir, outFileName, "9patch");
|
||||||
decode(inDir, inFileName, out9Patch, outFileName, "raw");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (! ext.equals(".xml")) {
|
if (! ext.equals(".xml")) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user