这里我的需求是:
1.对图片进行加密
2.要与AssetManager相结合
在搜索了网络上大部分的中文相关信息后,获得以下情报:
同时结合某个老外提到过的信息 https://www.it1352.com/117887.html
继承FileHandle,在其read方法时,对其后缀进行判断,如果是某特定后缀,进行加解密处理
相关类:
package com.zhfy.game.framework.tool; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.zhfy.game.config.ResConfig; import com.zhfy.game.framework.ComUtil; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; public class ImageFileHandle extends FileHandle { public ImageFileHandle(String fileName) { super(fileName); } @Override public InputStream read() { String suffix=super.file.getName(); suffix=suffix.substring(suffix.lastIndexOf('.')+1); Gdx.app.log("","bin:"+suffix); if(suffix.equals("tata")){ InputStream input = FileHandle.class.getResourceAsStream("/" + file.getPath().replace('\\', '/')); try { byte[] allBytes= ComUtil.toByteArray(input); int byteCount = 0; for (int i = 0; i < allBytes.length; i++) { // 每个字节异或密码,请保证解密时密码前后相同 byteCount++; if (byteCount <= 20) { // 加密20个字节,停止解密密 allBytes[i] ^= ResConfig.ImageKey.hashCode(); } else { allBytes[i] = allBytes[i]; } } input= new ByteArrayInputStream(allBytes); } catch (IOException e) { e.printStackTrace(); } return input; }else { return super.read(); } } }
package com.zhfy.game.framework.tool; import com.badlogic.gdx.assets.loaders.FileHandleResolver; import com.badlogic.gdx.files.FileHandle; public class ImageFileHandleResolver implements FileHandleResolver { @Override public FileHandle resolve(String fileName) { return new ImageFileHandle(fileName); } }
package com.zhfy.game; import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.assets.AssetManager; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.zhfy.game.framework.tool.ImageDecrypt; import com.zhfy.game.framework.tool.ImageFileHandleResolver; public class Armageddon extends ApplicationAdapter { SpriteBatch batch; Texture img; AssetManager am; boolean ifDraw; @Override public void create () { batch = new SpriteBatch(); //ImageDecrypt i=new ImageDecrypt(); am=new AssetManager(new ImageFileHandleResolver()); //img = i.getTexture("badlogic.bin"); am.load("badlogic.tata",Texture.class); ifDraw=false; } @Override public void render () { Gdx.gl.glClearColor(1, 0, 0, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); batch.begin(); if(ifDraw){ batch.draw(img, 0, 0); }else if(am.update()) { // updata()返回true,证明所有资源加载完成 // 可以执行对应的操作了 img=am.get("badlogic.tata",Texture.class); ifDraw=true; } batch.end(); } @Override public void dispose () { batch.dispose(); img.dispose(); am.dispose(); } }
public static byte[] toByteArray(InputStream input) throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); copy(input, output); return output.toByteArray(); } public static int copy(InputStream input, OutputStream output) throws IOException { long count = copyLarge(input, output); if (count > 2147483647L) { return -1; } return (int)count; } public static long copyLarge(InputStream input, OutputStream output) throws IOException { byte[] buffer = new byte[4096]; long count = 0L; int n = 0; while (-1 != (n = input.read(buffer))) { output.write(buffer, 0, n); count += n; } return count; }
import java.awt.BorderLayout; import java.awt.Button; import java.awt.Dialog; import java.awt.FileDialog; import java.awt.Frame; import java.awt.Graphics2D; import java.awt.Panel; import java.awt.TextArea; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JOptionPane; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.PixmapIO; /** * 加密GUI * * @author 原作者方国 * */ public class Encryption { private static Frame frame = new Frame("加密解密工具"); private TextField pathField = new TextField(20); private TextField keyField = new TextField(20); private JButton openButton = new JButton("加载文件"); private JButton setPasswordButton = new JButton("修改密码"); private JButton startButton = new JButton("开始加密"); private static TextArea informationArea = new TextArea(100, 60); private static FileDialog pathFileDialog = new FileDialog(frame, "选择要加密的文件", FileDialog.LOAD); private static String key = "key"; private static String path = "path"; public void init() { frame.setBounds(200, 200, 480, 270); keyField.setText("请修改密钥,默认为:" + "key"); openButton.addActionListener(new ActionListener() { // 加载文件 @Override public void actionPerformed(ActionEvent e) { pathFileDialog.setVisible(true); pathField.setText("" + pathFileDialog.getDirectory() + pathFileDialog.getFile()); path = pathField.getText(); } }); setPasswordButton.addActionListener(new ActionListener() { // 修改秘钥 @Override public void actionPerformed(ActionEvent arg0) { key =keyField.getText(); JOptionPane.showMessageDialog(null, " 恭喜您密钥修改成功!", "标题条文字串", JOptionPane.DEFAULT_OPTION); } }); startButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if ("path".equals(path)) { final Dialog tip = new Dialog(frame, true); tip.setTitle("请加载文件"); Button button = new Button("请加载文件"); tip.add(button); tip.setBounds(frame.getX() + frame.getWidth() / 2 - 70, frame.getY() + frame.getHeight() / 2 - 40, 140, 80); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { tip.setVisible(false); } }); tip.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { super.windowClosing(e); tip.setVisible(false); } }); tip.setVisible(true); } else { // 开始加密 File file = new File(pathFileDialog.getDirectory()); File[] files = file.listFiles(); for (int i = 0; i < files.length; i++) { File inFile = null; FileInputStream inputStream = null; File outFile = null; FileOutputStream outputStream = null; try { inFile = new File(files[i].getAbsolutePath()); inputStream = new FileInputStream(inFile); outFile = new File("" + pathFileDialog.getDirectory() + getFileNameNoEx("" + inFile.getName()) + ".tata"); while (outFile.exists()) { if (!outFile.exists()) { outFile.createNewFile(); } else { outFile = new File("" + pathFileDialog.getDirectory() + getFileNameNoEx("" + inFile.getName()) + i + ".tata"); break; } } outputStream = new FileOutputStream(outFile); int value = -1; // 加密20个字节 int byteCount = 0; while ((value = inputStream.read()) != -1) { // raf.write(value ^ key.hashCode()); // 每个字节异或密码,请保证解密时密码前后相同 byteCount++; if (byteCount <= 20) { // 加密20个字节,停止加密 outputStream.write(value ^ key.hashCode()); } else { outputStream.write(value); } } } catch (FileNotFoundException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } finally { try { outputStream.close(); inputStream.close(); } catch (Exception e1) { e1.printStackTrace(); } } informationArea.append(files[i].getName() + " 加密完成!\n"); } } } }); frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { super.windowClosing(e); System.exit(0); } }); Panel panel = new Panel(); panel.add(pathField); panel.add(openButton); Panel panel2 = new Panel(); panel2.add(keyField); panel2.add(setPasswordButton); panel2.add(informationArea); Panel panel3 = new Panel(); panel3.add(startButton); frame.add(panel, BorderLayout.NORTH); frame.add(panel2); frame.add(panel3, BorderLayout.SOUTH); frame.setVisible(true); } /** * 去掉文件扩展名 * * @param filename * @return */ public static String getFileNameNoEx(String filename) { if ((filename != null) && (filename.length() > 0)) { int dot = filename.lastIndexOf('.'); if ((dot > -1) && (dot < (filename.length()))) { return filename.substring(0, dot); } } return filename; } public static void main(String[] args) { new Encryption().init(); } public String lastName(File file){ if(file==null) return null; String filename = file.getName(); if(filename.lastIndexOf(".")==-1){ return "";//文件没有后缀名的情况 } //此时返回的是带有 . 的后缀名, return filename.substring(filename.lastIndexOf(".")); //return filename.subString(filename.lastIndexOf(".")+1);// 这种返回的是没有.的后缀名 // 下面这种如果对于String类型可能有问题,如 以.结尾的字符串,会报错。但是文件没有以点结尾的 } }
测试结果
-------------------------------------------------以上方法在安卓测试时会报找不到资源的错误,所以我参考另一种方法重写了loader方法,测试通过-------------------------------------------------
package com.zhfy.game.framework.tool; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.assets.AssetDescriptor; import com.badlogic.gdx.assets.AssetLoaderParameters; import com.badlogic.gdx.assets.AssetManager; import com.badlogic.gdx.assets.loaders.AsynchronousAssetLoader; import com.badlogic.gdx.assets.loaders.FileHandleResolver; import com.badlogic.gdx.assets.loaders.TextureLoader; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.utils.Array; import com.zhfy.game.config.ResConfig; public class EncryptTextureLoader extends AsynchronousAssetLoader<Texture, EncryptTextureLoader.TextureParameter> { public EncryptTextureLoader(FileHandleResolver resolver) { super(resolver); } @Override public Array<AssetDescriptor> getDependencies(String fileName, FileHandle file, TextureParameter parameter) { return null; } @Override public void loadAsync(AssetManager manager, String fileName, FileHandle file, TextureParameter parameter) { } @Override public Texture loadSync(AssetManager manager, String fileName, FileHandle file, TextureParameter parameter) { byte[] allBytes = file.readBytes(); int byteCount = 0; for (int i = 0; i < allBytes.length; i++) { // 每个字节异或密码,请保证解密时密码前后相同 byteCount++; if (byteCount <= 20) { // 加密20个字节,停止解密密 allBytes[i] ^= ResConfig.ImageKey.hashCode(); } else { allBytes[i] = allBytes[i]; } } Pixmap pixmap = new Pixmap(allBytes, 0, allBytes.length); Texture texture = new Texture(pixmap); Gdx.app.log("加载特殊资源",fileName); return texture; } public class TextureParameter extends AssetLoaderParameters<Texture> { } }
assetManager=new AssetManager(); assetManager.setLoader(Texture.class, ".tata", new EncryptTextureLoader(new InternalFileHandleResolver()) );
测试ok:
---------------------------------------------------------------------------------------------------------------------------------------------------
网络上的另一种实现思路:利用Assetmanager异步加载资源同时,以加载资源解密的实现实例:
https://download.csdn.net/download/kloxo/6504835?utm_source=bbsseo
由于相关配套文章丢失,只剩下源码(淘宝花了2块钱从csdn下载),所以发出给大家参考下
主源码:
package ghuazo.cipher; import com.badlogic.gdx.assets.AssetDescriptor; import com.badlogic.gdx.assets.AssetLoaderParameters; import com.badlogic.gdx.assets.AssetManager; import com.badlogic.gdx.assets.loaders.AsynchronousAssetLoader; import com.badlogic.gdx.assets.loaders.FileHandleResolver; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.TextureData; import com.badlogic.gdx.graphics.Pixmap.Format; import com.badlogic.gdx.graphics.Texture.TextureFilter; import com.badlogic.gdx.graphics.Texture.TextureWrap; import com.badlogic.gdx.graphics.g2d.Gdx2DPixmap; import com.badlogic.gdx.graphics.glutils.FileTextureData; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.GdxRuntimeException; /** * @author GhuaZo * * QQ:137336521 * * 声明一下: * 本DEMO来自LIBGDX开发交流群 :145353767,转载切勿删除本提示 */ public class CipherTextureLoader extends AsynchronousAssetLoader<Texture, CipherTextureLoader.TextureParameter>{ public CipherTextureLoader(FileHandleResolver resolver) { super(resolver); } static public class TextureLoaderInfo { String filename; TextureData data; Texture texture; }; TextureLoaderInfo info = new TextureLoaderInfo(); public void loadAsync (AssetManager manager, String fileName, FileHandle file, TextureParameter parameter) { info.filename = fileName; if (parameter == null || parameter.textureData == null) { Pixmap pixmap = null; Format format = null; boolean genMipMaps = false; info.texture = null; if (parameter != null) { format = parameter.format; genMipMaps = parameter.genMipMaps; info.texture = parameter.texture; } pixmap = this.decipherPixmap(file); info.data = new FileTextureData(file, pixmap, format, genMipMaps); } else { info.data = parameter.textureData; if (!info.data.isPrepared()) info.data.prepare(); info.texture = parameter.texture; } } public Texture loadSync (AssetManager manager, String fileName, FileHandle file, TextureParameter parameter) { if (info == null) return null; Texture texture = info.texture; if (texture != null) { texture.load(info.data); } else { texture = new Texture(info.data); } if (parameter != null) { texture.setFilter(parameter.minFilter, parameter.magFilter); texture.setWrap(parameter.wrapU, parameter.wrapV); } return texture; } private Pixmap decipherPixmap(FileHandle file){ Gdx2DPixmap pixmap = null ; short ff = (short)-128 ; try { byte[] bytes = file.readBytes(); for(int i=0 ; i<bytes.length ; i++){ bytes[i] = (byte) (bytes[i] ^ ff); } pixmap = new Gdx2DPixmap(bytes, 0, bytes.length, 0); } catch (Exception e) { throw new GdxRuntimeException("Couldn't load file: " + file, e); } return new Pixmap(pixmap) ; } static public class TextureParameter extends AssetLoaderParameters<Texture> { /** the format of the final Texture. Uses the source images format if null **/ public Format format = null; /** whether to generate mipmaps **/ public boolean genMipMaps = false; /** The texture to put the {@link TextureData} in, optional. **/ public Texture texture = null; /** TextureData for textures created on the fly, optional. When set, all format and genMipMaps are ignored */ public TextureData textureData = null; public TextureFilter minFilter = TextureFilter.Nearest; public TextureFilter magFilter = TextureFilter.Nearest; public TextureWrap wrapU = TextureWrap.ClampToEdge; public TextureWrap wrapV = TextureWrap.ClampToEdge; } @Override public Array<AssetDescriptor> getDependencies(String fileName, FileHandle file, TextureParameter parameter) { // TODO Auto-generated method stub return null; } }
package ghuazo.cipher; import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.assets.AssetManager; import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver; import com.badlogic.gdx.graphics.GL10; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.SpriteBatch; /** * @author GhuaZo * * QQ:137336521 * * 声明一下: * 本DEMO来自LIBGDX开发交流群 :145353767,转载切勿删除本提示 */ public class ImageCipher implements ApplicationListener { private SpriteBatch batch; private Texture texture; private AssetManager asset; @Override public void create() { this.batch = new SpriteBatch(); this.asset = new AssetManager(); this.asset.setLoader(Texture.class, ".cpr", new CipherTextureLoader(new InternalFileHandleResolver()) ); this.asset.load("data/ghuazo.cpr", Texture.class); } @Override public void dispose() { batch.dispose(); texture.dispose(); } @Override public void render() { Gdx.gl.glClearColor(1, 1, 1, 1); Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); if (this.asset.update()) { this.texture = this.asset.get("data/ghuazo.cpr", Texture.class); } if (this.texture != null) { batch.begin(); batch.draw(this.texture, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); batch.end(); } } @Override public void resize(int width, int height) { } @Override public void pause() { } @Override public void resume() { } }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit trunk//EN" "http://google-web-toolkit.googlecode.com/svn/trunk/distro-source/core/src/gwt-module.dtd"> <module> <source path="ghuazo/cipher" /> </module>
工具代码:
package ghuazo.cipher; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; /** * * @author GhuaZo * QQ:137336521 * * 群里面总有好多人在讨论图片加密的问题,其实个人觉得没有什么必要, * 主要觉得图片解密很浪费资源,特别我们开发手机程序的时候,尽量不用;但是有人提出了就写个简单的小demo给大家提个思路 * 关键是这个算法比较简单,运行时候消耗内存很小,缺点当然也有就是计算量(CPU)很大,这样涉及主要考虑到的就是手机游戏 * 一般资源加载都是在程序运行之前采用异步加载方式,加载属于IO操作,这时候手机内存利用很高,而CPU几乎没有耗时运算, * 所以我们将我们的解密代码加载时候运行,算法比较简单,但是对于一般的人足够用了。 * 声明一下: * 本DEMO来自LIBGDX开发交流群 :145353767,转载切勿删除本提示 */ public class CipherMaker { private static short FF = (short)-128 ; public static void main(String args[]) throws Exception{ //将123.jpg(明文)的图片加密到456.jpg(密文) cipher("D:\\123.png","D:\\ghuazo.png"); System.out.println("将123.jpg(明文)的图片加密到ghuazo.png(密文)"); //将456.jpg(密文)的图片解密成789.jpg(明文) //cipher("D:\\456.jpg","D:\\789.jpg"); //System.out.println("将456.jpg(密文)的图片解密成789.jpg(明文)"); } /** * 如果文件是明文文件输入,则输出加密的文件,如果输入的是加密的文件,则输出的是解密后的文件 * @param inputPath * @param outputPath * @throws Exception */ public static void cipher(String inputPath,String outputPath)throws Exception{ //配合追加模式写文件 File outFile = new File(outputPath); if(outFile.exists()){ outFile.delete(); } //追加模式写文件 InputStream input = new FileInputStream(inputPath) ; OutputStream output = new FileOutputStream(outputPath,true) ; byte[] cacheBytes = new byte[1024]; int cacheSize = 0; while ((cacheSize = input.read(cacheBytes)) > 0) { for(int i=0 ; i<cacheSize ; i++){ cacheBytes[i] = (byte) (cacheBytes[i] ^ FF); } output.write(cacheBytes, 0, cacheSize); } input.close() ; output.close() ; } }
ps:该功能实现耗时3天.心累,还是推荐使用其他资料多的引擎吧,中文资料实在太少了.