ZenBrush(禅宗画笔)反编译后二次开发(电子签名_毛笔带笔锋的效果)
2017-12-07 17:47
561 查看
最近POS机项目有个需求,电子签名,就是一个画板上签名。看了一些文章,把贝塞尔曲线算法用上了效果也
不怎么样,然后在Github上也找了些demo,有个brushes还不错,但没有毛笔笔锋那种效果,找啊找,终于找到
ZenBrush(中文名:禅宗画笔,好像是日本人开发的),的确做的非常好,但没有sdk,也没有开源。
想着能否通过反编译的方式,复用里面的核心代码!通过几天尝试可行,但是,如果二次开发的apk挂在了复用里
面的代码时,就没办法通过java代码修复了,只能通过smail修复了,这个需要反编译相关知识了。
来张毛笔带笔锋的效果图,没有书法功底别见笑。
现在分析下二次开发思路:
1. 写一个打log的文件ApktoolLog.java,方便在反编译的smail文件中注入代码(通常一行,根据需要在
ApktoolLog加函数,争取注入时只要一行代码),下面贴下ApktoolLog.java的简易代码(但很实用哦)
将它反编译成smail文件ApktoolLog.smali拷贝到对应目录。
2.反编译原始的ZenBrush.apk在smail中注入log代码,得到关键参数值,下面详解设置画笔样式的关键参数
找到设置画笔样式代码在ZenBrush/smali/jp/co/psoft/zenbrushfree/library/a.smali:209
smail和Java Decompiler对比,然后在smail的209行上面注入log代码把v1的值打印出来,就知道ZenBrush.apk
当前设置样式的参数了
.method private c(Ljp/co/psoft/zenbrushfree/library/b;)V
.locals 2
iget-object v0, p0, Ljp/co/psoft/zenbrushfree/library/a;->g:Ljp/co/psoft/ZenBrushLib/ZenBrushRenderer;
invoke-virtual {p1}, Ljp/co/psoft/zenbrushfree/library/b;->a()I
move-result v1
invoke-static {v1}, Lcom/example/helloworld/ApktoolLog;->e(I)V
invoke-virtual {v0, v1}, Ljp/co/psoft/ZenBrushLib/ZenBrushRenderer;->setBrushType(I)V
iget-object v0, p0, Ljp/co/psoft/zenbrushfree/library/a;->g:Ljp/co/psoft/ZenBrushLib/ZenBrushRenderer;
iget v1, p1, Ljp/co/psoft/zenbrushfree/library/b;->g:F
invoke-virtual {v0, v1}, Ljp/co/psoft/ZenBrushLib/ZenBrushRenderer;->setBrushAlpha(F)V
iput-object p1, p0, Ljp/co/psoft/zenbrushfree/library/a;->d:Ljp/co/psoft/zenbrushfree/library/b;
iget-object v0, p0, Ljp/co/psoft/zenbrushfree/library/a;->h:[I
invoke-virtual {p1}, Ljp/co/psoft/zenbrushfree/library/b;->ordinal()I
move-result v1
aget v0, v0, v1
invoke-direct {p0, v0}, Ljp/co/psoft/zenbrushfree/library/a;->b(I)V
return-void
.end method
然后回编,签名,安装运行抓log,过滤关键字,就能一一找到关键值的参数。
3.
在自己的工程里建同名的包名,类名,函数,可以在Java Decompiler里直接拷贝,将函数体去掉,如果有
返回值的,随便return一个对应值即可。如核心文件ZenBrushRenderer代码
4.
编写自己的文件,并调用复用的文件及函数,这一步很复杂,需要对比smail及Java Decompiler的java代码,
并找到调用关系,这个需要反编译相关知识,不懂得略过吧,下面贴出我自己的MainActivity
拓展,POS机打印电子签名,有响应的SDK打印Bitmap(可能需要旋转,放缩等动作)即可
效果图如下:
打印效果还可以哈
附件下载(包括MyZenBrush(不支持打印),MyZenBrush_ESCPrinter(支持打印),ZenBrush_smail(复用核心代码))
不怎么样,然后在Github上也找了些demo,有个brushes还不错,但没有毛笔笔锋那种效果,找啊找,终于找到
ZenBrush(中文名:禅宗画笔,好像是日本人开发的),的确做的非常好,但没有sdk,也没有开源。
想着能否通过反编译的方式,复用里面的核心代码!通过几天尝试可行,但是,如果二次开发的apk挂在了复用里
面的代码时,就没办法通过java代码修复了,只能通过smail修复了,这个需要反编译相关知识了。
来张毛笔带笔锋的效果图,没有书法功底别见笑。
现在分析下二次开发思路:
1. 写一个打log的文件ApktoolLog.java,方便在反编译的smail文件中注入代码(通常一行,根据需要在
ApktoolLog加函数,争取注入时只要一行代码),下面贴下ApktoolLog.java的简易代码(但很实用哦)
package com.example.helloworld; import android.util.Log; public class ApktoolLog { private static final String TAG = "ApktoolLog"; public static void printStackTrace() { for (StackTraceElement i : Thread.currentThread().getStackTrace()) { Log.e(TAG, "" + i); } } public static void e(String info) { Log.e(TAG, "String:" + info); } public static void e(int info) { Log.e(TAG, "int:" + info); } public static void e(long info) { Log.e(TAG, "long:" + info); } public static void e(float info) { Log.e(TAG, "float:" + info); } public static void e(double info) { Log.e(TAG, "double:" + info); } public static void e(float paramFloat1, float paramFloat2, float paramFloat3, float paramFloat4, float paramFloat5, float paramFloat6, float paramFloat7, float paramFloat8) { Log.e(TAG, "paramFloat1:" + paramFloat1 + ", paramFloat2:" + paramFloat2 + ", paramFloat3:" + paramFloat3 + ", paramFloat4:" + paramFloat4 + ", paramFloat5:" + paramFloat5 + ", paramFloat6:" + paramFloat6 + ", paramFloat7:" + paramFloat7 + ", paramFloat8:" + paramFloat8); } }
将它反编译成smail文件ApktoolLog.smali拷贝到对应目录。
2.反编译原始的ZenBrush.apk在smail中注入log代码,得到关键参数值,下面详解设置画笔样式的关键参数
$ grep -rn "setBrushType" ZenBrush 匹配到二进制文件 ZenBrush/build/apk/lib/armeabi-v7a/libZenBrushRenderer.so 匹配到二进制文件 ZenBrush/build/apk/lib/armeabi/libZenBrushRenderer.so ZenBrush/smali/jp/co/psoft/zenbrushfree/library/a.smali:209: invoke-virtual {v0, v1}, Ljp/co/psoft/ZenBrushLib/ZenBrushRenderer;->setBrushType(I)V ZenBrush/smali/jp/co/psoft/ZenBrushLib/ZenBrushRenderer.smali:528:.method public native setBrushType(I)V
找到设置画笔样式代码在ZenBrush/smali/jp/co/psoft/zenbrushfree/library/a.smali:209
smail和Java Decompiler对比,然后在smail的209行上面注入log代码把v1的值打印出来,就知道ZenBrush.apk
当前设置样式的参数了
.method private c(Ljp/co/psoft/zenbrushfree/library/b;)V
.locals 2
iget-object v0, p0, Ljp/co/psoft/zenbrushfree/library/a;->g:Ljp/co/psoft/ZenBrushLib/ZenBrushRenderer;
invoke-virtual {p1}, Ljp/co/psoft/zenbrushfree/library/b;->a()I
move-result v1
invoke-static {v1}, Lcom/example/helloworld/ApktoolLog;->e(I)V
invoke-virtual {v0, v1}, Ljp/co/psoft/ZenBrushLib/ZenBrushRenderer;->setBrushType(I)V
iget-object v0, p0, Ljp/co/psoft/zenbrushfree/library/a;->g:Ljp/co/psoft/ZenBrushLib/ZenBrushRenderer;
iget v1, p1, Ljp/co/psoft/zenbrushfree/library/b;->g:F
invoke-virtual {v0, v1}, Ljp/co/psoft/ZenBrushLib/ZenBrushRenderer;->setBrushAlpha(F)V
iput-object p1, p0, Ljp/co/psoft/zenbrushfree/library/a;->d:Ljp/co/psoft/zenbrushfree/library/b;
iget-object v0, p0, Ljp/co/psoft/zenbrushfree/library/a;->h:[I
invoke-virtual {p1}, Ljp/co/psoft/zenbrushfree/library/b;->ordinal()I
move-result v1
aget v0, v0, v1
invoke-direct {p0, v0}, Ljp/co/psoft/zenbrushfree/library/a;->b(I)V
return-void
.end method
然后回编,签名,安装运行抓log,过滤关键字,就能一一找到关键值的参数。
3.
在自己的工程里建同名的包名,类名,函数,可以在Java Decompiler里直接拷贝,将函数体去掉,如果有
返回值的,随便return一个对应值即可。如核心文件ZenBrushRenderer代码
package jp.co.psoft.ZenBrushLib; import android.opengl.GLSurfaceView; import android.view.MotionEvent; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; public class ZenBrushRenderer implements GLSurfaceView.Renderer { static { System.loadLibrary("ZenBrushRenderer"); } public ZenBrushRenderer() { } private native void onNdkDrawFrame(); private native void onNdkSurfaceChanged(int paramInt1, int paramInt2); public final int a() { return 0; } public final boolean a(MotionEvent paramMotionEvent) { return true; } public final int b() { return 0; } public native boolean canClearInk(); public native boolean canRedo(); public native boolean canUndo(); public native boolean canvasInitialize(int paramInt1, int paramInt2); public native boolean canvasUpdate(int paramInt, float paramFloat1, float paramFloat2, float paramFloat3, double paramDouble); public native void clearInk(); public native boolean getImageARGB8888(int[] paramArrayOfInt); public native boolean getInkImageARGB8888(int[] paramArrayOfInt); public void onDrawFrame(GL10 paramGL10) { } public void onSurfaceChanged(GL10 paramGL10, int paramInt1, int paramInt2) { } public void onSurfaceCreated(GL10 paramGL10, EGLConfig paramEGLConfig) { } public native void redo(); public native void setBackgroundColor(float paramFloat1, float paramFloat2, float paramFloat3); public native boolean setBackgroundImageARGB8888(int[] paramArrayOfInt, int paramInt1, int paramInt2); public native void setBrushAlpha(float paramFloat); public native void setBrushSize(float paramFloat); public native void setBrushTintColor(float paramFloat1, float paramFloat2, float paramFloat3, float paramFloat4, float paramFloat5, float paramFloat6, float paramFloat7, float paramFloat8); public native void setBrushType(int paramInt); public native boolean setInkImageARGB8888(int[] paramArrayOfInt); public native void undo(); }jni的相关函数声明及部分成员函数
4.
编写自己的文件,并调用复用的文件及函数,这一步很复杂,需要对比smail及Java Decompiler的java代码,
并找到调用关系,这个需要反编译相关知识,不懂得略过吧,下面贴出我自己的MainActivity
package com.example.helloworld; import java.util.concurrent.FutureTask; import com.example.helloworld.utils.BitmapUtils; import jp.co.psoft.ZenBrushLib.ZenBrushGLSurfaceView; import jp.co.psoft.ZenBrushLib.ZenBrushRenderer; import jp.co.psoft.zenbrushfree.library.e; import jp.co.psoft.zenbrushfree.library.i; import jp.co.psoft.zenbrushfree.library.n; import android.app.Activity; import android.graphics.Bitmap; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.text.TextUtils; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.WindowManager; import android.widget.Button; import android.widget.Toast; public class MainActivity extends Activity implements OnClickListener, i { private ZenBrushGLSurfaceView mBrushGLSurfaceView; private Button mBtnClear; private Button mBtnUndo; private Button mBtnRedo; private float mBrushSize = 10.0f; private TYPE_CALLBACK mTypeCallback = TYPE_CALLBACK.NONE; private enum TYPE_CALLBACK { NONE, SAVE, PRINT } private static final int EVENT_SHOW_TOAST = 1; private Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case EVENT_SHOW_TOAST: Toast.makeText(MainActivity.this, (String) msg.obj, Toast.LENGTH_LONG).show(); break; } } }; @Override protected void onCreate(Bundle bundle) { super.onCreate(bundle); setContentView(R.layout.activity_main); try { getWindow().addFlags( WindowManager.LayoutParams.class.getField( "FLAG_NEEDS_MENU_KEY").getInt(null)); } catch (NoSuchFieldException e) { } catch (IllegalAccessException e) { } mBrushGLSurfaceView = (ZenBrushGLSurfaceView) findViewById(R.id.view); // 设置画布背景颜色 mBrushGLSurfaceView.a.setBackgroundColor(255F, 255F, 255F); // 设置画笔颜色 mBrushGLSurfaceView.a.setBrushTintColor(0f, 0f, 0f, 1.0f, 0f, 0f, 0f, 1.0f); // 设置画笔类型 mBrushGLSurfaceView.a.setBrushType(0); mBrushGLSurfaceView.a.setBrushAlpha(1.0f); // 设置画笔大小 mBrushGLSurfaceView.a.setBrushSize(mBrushSize); mBtnClear = (Button) findViewById(R.id.clear_lnk); mBtnUndo = (Button) findViewById(R.id.undo); mBtnRedo = (Button) findViewById(R.id.redo); mBtnClear.setOnClickListener(this); mBtnUndo.setOnClickListener(this); mBtnRedo.setOnClickListener(this); } @Override public void onClick(View view) { // TODO Auto-generated method stub if (view == mBtnClear) { mBrushGLSurfaceView.a.clearInk(); } else if (view == mBtnUndo) { mBrushGLSurfaceView.a.undo(); } else if (view == mBtnRedo) { mBrushGLSurfaceView.a.redo(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // TODO Auto-generated method stub getMenuInflater().inflate(R.menu.main_menu, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { // TODO Auto-generated method stub switch (item.getItemId()) { case R.id.action_save: mTypeCallback = TYPE_CALLBACK.SAVE; new e(mBrushGLSurfaceView).a(this); break; case R.id.action_print: mTypeCallback = TYPE_CALLBACK.PRINT; new e(mBrushGLSurfaceView).a(this); break; case R.id.action_brush_size_increase: mBrushSize += 1; mBrushGLSurfaceView.a.setBrushSize(mBrushSize); Toast.makeText(this, "Set brush size : " + mBrushSize, Toast.LENGTH_SHORT).show(); break; case R.id.action_brush_size_decrease: mBrushSize -= 1; mBrushGLSurfaceView.a.setBrushSize(mBrushSize); Toast.makeText(this, "Set brush size : " + mBrushSize, Toast.LENGTH_SHORT).show(); break; } return super.onOptionsItemSelected(item); } private Bitmap getGLBitmap() { int width = mBrushGLSurfaceView.getWidth(); int height = mBrushGLSurfaceView.getHeight(); Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); int[] pixels = new int[width * height]; mBrushGLSurfaceView.a.getImageARGB8888(pixels); bitmap.setPixels(pixels, 0, width, 0, 0, width, height); return bitmap; } @Override public void a(n paramn) { // TODO Auto-generated method stub Bitmap bitmap = BitmapUtils.createBitmap(paramn); switch (mTypeCallback) { case SAVE: String fileName = BitmapUtils.saveBitmapAsPng(BitmapUtils .adjustPhotoRotation(bitmap, 90)); if (!TextUtils.isEmpty(fileName)) { mHandler.sendMessage(mHandler.obtainMessage(EVENT_SHOW_TOAST, fileName)); } break; case PRINT: break; } } }
拓展,POS机打印电子签名,有响应的SDK打印Bitmap(可能需要旋转,放缩等动作)即可
@Override public void a(n paramn) { // TODO Auto-generated method stub Bitmap bitmap = BitmapUtils.createBitmap(paramn); switch (mTypeCallback) { case SAVE: String fileName = BitmapUtils.saveBitmapAsPng(BitmapUtils .adjustPhotoRotation(bitmap, 90)); if (!TextUtils.isEmpty(fileName)) { mHandler.sendMessage(mHandler.obtainMessage(EVENT_SHOW_TOAST, fileName)); } break; case PRINT: mESCPrinter.sendBitmap( 0, BitmapUtils.scaleBitmap(bitmap, 1.0f * ESCPrinter.PAGE_WIDTH / bitmap.getWidth())); break; } }
效果图如下:
打印效果还可以哈
附件下载(包括MyZenBrush(不支持打印),MyZenBrush_ESCPrinter(支持打印),ZenBrush_smail(复用核心代码))
相关文章推荐
- 基于ArcGIS Engine+C#的二次开发---电子校园管理信息系统(2)
- qgis二次开发之编译
- 【android开发】手写签名系统的设计与实现之实现pdf文件上手写签名效果(五)---完
- 开发Android第三步,签名证书,真机安装编译程序
- 波克城市棋牌源码,傲翼棋牌源码3D人物效果!(本人二次开发)
- UG 二次开发 dll 签名
- [github项目]基于百度地图二次开发实现的车辆监管(包含车辆定位、车辆图片和方向控制,电子围栏,图形绘制等功能)前端实现(不包含后端实现)
- ecshop二次开发--电子票
- Openstack 二次开发之:在windows 环境下编译Openstack-java-sdk
- eclipse源码编译的apk开发,使用系统签名机制.
- 【android开发】手写签名系统的设计与实现之实现画笔设置(四)
- 毛笔算法 毛笔签名效果
- 开发Android第三步,签名证书,真机安装编译程序
- Android应用开发之APK文件电子签名
- eclipse源码编译的apk开发,使用系统签名机制. (可让自己的写的apk有权限卸载其他应用)
- SIM800C32嵌入式二次开发编译环境的搭建
- 使用贝塞尔曲线算法实现毛笔签名效果
- qgis二次开发之编译