InShot(com.camerasideas.instashot)图像编辑与滤镜渲染技术实现分析报告

反编译源码根目录:/Users/ccc/Downloads/app/out/jadx_src/sources/(资源在 .../resources/assets/、原生库在 .../resources/lib/)。 代码经过混淆,jadx 用 JADX INFO: compiled from: 原名.java 保留了原始类名,下文"原名"即据此还原;所有路径省略公共前缀 sources/


1. 图像编辑主界面 ImageEditActivity:UI 组织与编辑状态管理

1.1 UI 组织(双视图架构:GL 渲染层 + 交互覆盖层)

com/camerasideas/instashot/ImageEditActivity.java(1441 行)继承 AbstractEditActivity(instashot/AbstractEditActivity.java),后者继承 X3/A.java = BaseMvpActivity(MVP 基类)。布局 R.layout.activity_image_edit 精简为两层:

<FrameLayout id=full_screen_fragment_container>          <!-- 根,承载全屏 Fragment -->
  <FrameLayout id=surfaceview_container>
    <SurfaceView id=image_surfaceview/>                  <!-- GL 预览渲染面 -->
  </FrameLayout>
  <com.camerasideas.instashot.widget.ImageEditLayoutView id=edit_layout/>
  <include .../fragment_collage_swap_title, full_screen_mask_layout, discard_image_works_layout/>
  <com.camerasideas.instashot.widget.EditMenu id=edit_menu/>
</FrameLayout>

关键中间层 ImageEditLayoutView(widget/ImageEditLayoutView.java,extends LinearLayout,实现 GestureDetector.OnGestureListener, P8.d),构造时 inflate image_edit_layout_view.xml 并持有整个编辑器画布:

// ImageEditLayoutView.java:163-165
LayoutInflater.from(context).inflate(R.layout.image_edit_layout_view, this);
this.f32215f = (ItemView) findViewById(R.id.item_view);   // 画布交互层
// 布局内另有 middle_layout(DragFrameLayout)、bottom_layout/bottom_parent_layout(Fragment 容器)、
// menu_dimmer、banner_container、view_spring_help

AbstractEditActivity 用 ButterKnife 绑定 20+ 控件(mItemView/mEditLayout/mMiddleLayout/mSurfaceView/mEditMenu/mBtnUndo/mBtnRedo/mBtnCompare/mBtnOpReset/mProgressBar/...),并持有 C3781h f24842t = C3781h.a(this)(GL 上下文)与 C1730i(图形项管理器);onCreate 中把 GL 表面与画布交给 Presenter:

// AbstractEditActivity.java:583-584
this.f24842t = C3781h.a(this);
((C3773e0) this.i).C1(this.mSurfaceView, this.mItemView);

1.2 编辑状态管理:MVP + 命令/备忘录撤销重做

MVP 三层:

撤销/重做 = 命令 + 备忘录模式(p126g4/ 包):

// BackForward.java — undo 栈操作
k kVarPop = this.f53204g.pop();
this.f53205h.push(kVarPop);
kVarLastElement = this.f53204g.lastElement();
...
lVar.b(kVar);                       // OpDataRollBack 执行回滚
this.f53207k.Mc(kVar);              // 通知 OnBackForwardListener
T.a(new k1());                      // EventBus 通知 UI 刷新
// PhotoOpDataCreator.a()
a10.f3161b = c1731j.clone();                       // GridContainerItem 深拷贝
for (AbstractC1723b b : list) {
    if (b instanceof V) arrayList2.add((V) b.R0());    // StickerItem/TextItem/PipItem 各自 clone()
}

菜单操作分发:ImageEditPresenter.d2(C4554d1) 是巨型 switch(操作码 21/24/36/38/41/65…),把 EditMenu(widget/EditMenu.java,FrameLayout + setEditType + OnEditClickListener)点击映射为具体编辑动作;Activity 只做薄壳:onClickthis.i.Y1()(保存)、V1()(重置/Fit)、z0()/F0()(撤销/重做)、d2(...)(菜单)。

设计模式:MVP(每个编辑功能 = Fragment + Presenter 对)、命令模式 + 备忘录(Gson 快照)、EventBus 事件驱动、观察者(p126g4/h.java OnBackForwardListener / p126g4/j.java OnStackChangeListener)、状态标志位(编辑类型 P0()、拼贴/画中画/滤镜模式 R0()/S0()/W0()/T0())。 Fragment 生命周期联动:ImageEditActivity.f 内部类实现 FragmentManager.m,在 Fragment 创建/销毁时控制菜单显隐。


2. com.camerasideas.graphicproc/(97 个类):自研图形处理框架

结论:这是 InShot 官方开源 "GraphicsDrawable" 架构的完整自研版——画布-条目模型:编辑器画布由若干可序列化、可变换的图形条目(BaseItem 体系)组成,条目各自绘制,管理器统一调度,GL 合成,草稿经 Gson 序列化。

2.1 entity/(14 类,全部可 Gson 序列化)

文件 原名 职责
a.java CaptionTextTemplate 字幕模板
b.java CaptionsTextProperty 字幕属性
c.java CaptionsTextProperty_Legacy 兼容旧版字幕
d.java CompareProperty 对比(原图/效果)
e.java EdgeBlendProperty 边缘融合
f.java EnhanceProperty 画质增强
g.java TextBendEffect 文字弯曲
h.java TextGlowEffect 文字发光
i.java TextProperty 文字属性
j.java TextProperty(kt) 文字属性 Kotlin 版
k.java TextUnderlineEffect 文字下划线
l.java TrackingInfo 跟踪信息
m.java TrackingProperty 跟踪属性
OutlineProperty.java OutlineProperty 抠图蒙版 + 描边(f24039d=蒙版路径、f24042g=isAI、i=编辑版本计数、f24036a/b/c=描边样式/宽度/颜色;Parcelable + Gson OLP_*)

所有字段带 @Jb.c("XX_xx") 注解——Jb/c.java 即 Gson SerializedName(重打包),这是草稿持久化的字段名稳定契约。

2.2 graphicsitems/(60 类,核心条目体系)

混淆名 原名 职责
C1731j GridContainerItem 拼贴容器(持有 GridImageItem 列表与布局)
C1733l GridImageItem 格内图片(滤镜/增强属性挂载点)
AbstractC1743w ImageItem 图片条目基类
V StickerItem 贴纸/涂鸦结果
X TextItem 文字
K PipItem 画中画
C MosaicItem 马赛克
C1722a AnimationItem 动效条目(多帧)
AbstractC1724c BorderItem 边框
b0 WatermarkItem 水印(总在顶层)
N Placeholder 占位
// GraphicItemManager.b() — 按类型入分类列表
if (abstractC1723b instanceof X) this.f24352c.add(abstractC1723b);
else if (C1746z.i(...) || C1746z.b(...)) this.f24353d.add(abstractC1723b);
else if (abstractC1723b instanceof C) this.f24354e.add(abstractC1723b);
else if (abstractC1723b instanceof K) this.f24355f.add((K) abstractC1723b);
... // 水印始终沉底:list.remove(b0Var); list.add(this.f24356g);

C1729h 是它的回调适配器(实现 F3.D.a,把渲染完成回调转给 Presenter)。

// ItemView.java:771
C1734m.f24393b.f24394a = new C1740t(context, new i());

2.3 utils/(17 类)

文件 原名 职责
f.java DataSourceProvider 泛型条目数据源(插入槽位搜索 b(Map,...)、变更通知)
n.java ModelLoader 异步模型下载(URL/md5 校验/进度回调,main Handler)
c.java CutoutHelper 抠图引擎门面(调 PortraitMatting)
h.java EdgeBlendLayoutUtils 边缘融合布局
i.java EmptyDataSourceProvider 空数据源
j.java ItemAnimator 条目动画
k.java ItemDrawContext 给条目注入 FrameBufferRenderer/shader 表等 GL 访问器
l.java LayoutShuffleUtils 拼贴布局打乱
q.java PortraitMattingModelLoaderFactory 抠图模型加载工厂
r.java SimpleListChangedCallback 列表变化回调
s.java TextItemKeyFrameHelper 文字关键帧
t.java TrackingUtil 跟踪工具
a.java AnimationProgressConverter 动画进度转换
g.java DataSourceSupplementProvider 数据补充接口

2.4 gson/(4 类)

MatrixTypeConverterPaintTypeConverter(实现 Gson JsonSerializer/JsonDeserializer)、RectFTypeAdapter(Kotlin)、UriTypeConverter。注册点:E3/a.java = GsonBuilderFactory:

// E3/a.java:171 — 编辑器专用 Gson
new GsonBuilder()
  .registerTypeAdapter(Uri.class, new UriTypeConverter())
  .registerTypeAdapter(RectF.class, new RectFTypeAdapter())
  .registerTypeAdapter(Matrix.class, new MatrixTypeConverter())
  .excludeFieldsWithModifiers(16, 128, 8)
  .setExclusionStrategies(new i())
  .registerTypeAdapter(C1731j.class, new h(context))   // 每个条目一个 InstanceCreator(带 Context 重建)
  .registerTypeAdapter(C1733l.class, new g(context))
  .registerTypeAdapter(X.class, new f(context))
  .registerTypeAdapter(V.class, new e(context))
  .registerTypeAdapter(K.class, new d(context))
  .registerTypeAdapter(C1722a.class, new c(context))
  .registerTypeAdapter(C.class, new b(context))
  .registerTypeAdapter(b0.class, new C0032a(context))
  .registerTypeAdapterFactory(new TextAttributesTypeAdapterFactory())

草稿落盘链路:p010a7/t.java = SaveDraftBuilder + p010a7/d.java = BaseWorkspace(抽象),由 BaseEditPresenter(Y5/b.java)在 H0()(退出编辑)时调用 tVar.b(dVar, c1773e0D0) 保存。

2.5 exception/(12 类)

防御性错误体系:BlackImageException(黑屏检测)、GLOutOfMemoryErrorWrongFilterSizeExceptionCreateGifDecoderExceptionCreateGifBufferExceptionResetItemRatioExceptionItemIllegalStateExceptionRendererExceptionDrawTextLabelExceptionInfinityExceptionDebugPostExceptionAnimEngineException——渲染/导出失败被分类上报(T3/k.b/c 处理)。


3. 滤镜系统:GPUImage + GLSL 着色器 + LUT

3.1 GPUImage 框架(第三方移植 + 深度改造)

static { System.loadLibrary("gpuimage-library"); System.loadLibrary("gpuimage-extension"); }
public static native void YUVtoARBG(byte[], int, int, int[]);
public static native byte[] aesDecrypt(Context, AssetManager, String);   // 素材 AES 解密
public static native byte[] aesDecryptFile(Context, String);
public static native String getShader(Context, int key);                // 原生 shader 缓存

3.2 滤镜链构建(Composite + 责任链/流水线)

编辑器顶层滤镜 = com/inshot/graphics/extension/Q.java = GPUImageEditorFilter extends GPUImageFilterGroup,由两条子链复合:

GPUImageFilterGroup.onDraw乒乓 FBO 串起整条链(每级从 Ph/b.java FrameBufferCache 取 FBO,输出纹理作为下一级输入)。离屏应用:I4/O.java = ImageFilterApplyer(用 PixelBuffer + GPUImageRenderer 逐滤镜渲染缩略图);实时预览经 C3/f.java = ImageFilterConverter(实现 a(inTex, outFbo))挂在 GridImageItem 的 ItemDrawContext 上,GLTextureView 变体用于 p088e6/C3787j(GLPipCropRenderer)。

3.3 LUT 滤镜实现(三套)

(1) 经典 2D LUT:com/inshot/graphics/extension/U.java = GPUImageLookupFilter extends GPUImageTwoInputFilter,内联标准 512×512 8×8 网格 fragment shader(blueColor*63.0 + mix(newColor1,newColor2,fract(blueColor)) + intensity uniform),纹理经 Ph/i.java(OpenGlUtils)glTexImage2D 上传,onDrawArraysPreglUniform1f 设强度。

(2) AES 加密 LUT 资产:GPUFilterChainGroup.d()(C3582z.java)用原生解密后解码 PNG,并复用 p135gd/g.java(SoftReference Bitmap 缓存 + inBitmap):

byte[] bArrAesDecryptFile = C0659v.q(strZ)
    ? GPUImageNativeLibrary.aesDecryptFile(context, strZ)
    : GPUImageNativeLibrary.aesDecrypt(context, context.getAssets(), strZ);
...
this.f47584c.setBitmap(bitmapDecodeByteArray, false);   // 喂给 GPUImageLookupFilter

链式组装按 Nb/h.java(FilterProperty)条件追加:if (hVar.L()) { u10.f46653b = hVar.j(); arrayList.add(u10); }(LUT+强度)→ 锐化 GPUImageSharpenFilterV2GPUImageToolFilterGPUMultiBandHsvFilterGPUImageToneCurveFilterV2 → 自动调色。

(3) AI 自动调色 3 输入 LUT(GLES 3.0):U1/d.java = GPUBaseAdjustLookupFilter(顶点 shader 声明 4 组纹理坐标,uniform inputImageTexture2/3/4w1..w3/intensity),两个子类:

选择策略在 Pc/i.java = GPUImageAutoAdjustProxyFilter:

if (AIAutoAdjust.d(this.mContext))       // 设备 3D 纹理能力探测
    this.f8760a = new U1.a(context, p129g7.a.f(context, "GPU3DLookupFilter.glsl"));
else
    this.f8760a = new U1.b(context2, p129g7.a.f(context2, "GPUAdjustLookupFilter.glsl"));
this.f8760a.init();

两个 shader 均确认存在于 resources/assets/GPU3DLookupFilter.glsl(#version 300 es + #extension GL_OES_texture_3D + sampler3D + dimension=33.0)与 resources/assets/GPUAdjustLookupFilter.glsl(2D 33 网格 + uniform vec2 size)。LUT 权重/路径存于 Nb/c.java = AutoAdjustProperty(lutPaths f8040g size==3、w1..w3intensityautoAdjustSwitch),LUT 系数由原生 AIAutoAdjust.getLutCoefNative(经 GLPixelReader 读回)计算。LUT 的 .png 文件不随包,运行时下载/加密存储。

3.4 编辑器应用

fragment/image/ImageFilterFragment.java(mFilterGroupTab/mAdjustSeekBar/mFilterList)+ p088e6/C3829x0.java = ImageFilterPresenter;拖动滑块 → I4/N.java = FilterUtils 把索引映射到 FilterProperty 各 setter → ImageFilterPresenter.v1(...) 推送 AutoAdjustProperty → 实时重渲染。缩略图:adapter/imageadapter/ImageFilterAdapter.java 内 AsyncTask 共享 I4/O ImageFilterApplyer,FilterProperty.L() 用滤镜名充当 LUT id。

设计模式:Composite(FilterGroup 树 + EditorFilter 复合)、Factory(C3562u 特效工厂 + Pc/i LUT 代理)、责任链/流水线(乒乓 FBO)、命令队列(runOnDraw(Runnable) 在 GL 线程 onDrawFrame 排空)、缓存(GLProgramCache ThreadLocal + 原生 getShader + LookupBitmap SoftReference)、MVP + 可序列化参数对象(Nb/h FilterProperty / Nb/e EffectProperty,@Jb.c("FP_xx"))。


4. 图像处理库封装:libImageKit.so / libYXAImg.so

4.1 libImageKit.so(Skia 渲染引擎,纹理型 JNI)

com/inshot/graphics/engine/SkiaImageEngine.java 是唯一加载点,实现 Kc/a.java(Engine 接口):

static { System.loadLibrary("ImageKit"); }
private final long mNativeRenderer = nInit();          // native 句柄,构造即建
private native boolean nBindOutputTarget(long j10, int i, int i10, int i11); // FBO id
private native SkiaBaseLayer nCreateLayer(long j10, int i);   // 1=border 2=image
private native void nDraw(long j10);
private native void nRelease(long j10);
private native void nSetClearColor(long j10, int i);

边界上不传 Bitmap——只传 long 指针、GL 纹理/FBO id、float[] 矩阵;Java 侧用 Ph/b.java = FrameBufferCache(LRU,key=w/h/format,ThreadLocal 单例,预算 min(512MB, heap/8))+ Ph/j = TextureFrameBuffer 管理 FBO,draw() 后恢复 GLES 状态。配套 layer/ 包(27 类):

唯一消费方:layer/p.java = LayoutLayer,经 Kc/b.java = ISCompositingEngine(包装 SkiaImageEngine + 输出 Size + HashMap<Class, ISLayer>)用于拼贴/布局渲染。

4.2 libYXAImg.so + libYXAIEngine.so(CPU 算法,位图型 JNI)

9 个 com/android/** 算法类全部继承 com/android/algo/base/b.java = BaseLibrary,其 loadLibrary() 统一加载并带授权校验:

public void loadLibrary() {
    ...
    libraries.add("MNN"); libraries.add("YXAIEngine"); libraries.add("YXAImg");
    for (String lib : libraries) V8.c.a(this.mContext, lib);   // ReLinker 加载
    this.mLibLoaded = true;
}

消费方(这些直接传 Bitmap/byte[],并经 CerChecker(libcer.so)证书 + SHA1 校验后才 initNative(...),属商业授权 SDK 门面):

能力 关键 native
com/android/inpaint/InPaint.java 修复/去水印 repairImageWithMaskNative(long,Bitmap,Bitmap,Bitmap),错误码 5376-5382
com/android/algo/caption/Caption.java 字幕识别 nativeDetect(long,Bitmap)->int[]
com/android/algo/videoSeg/VidSeg.java 视频分割 nativeGetFrameMask(long,Bitmap,Bitmap,...)
com/android/algo/vidSot/VidSot.java 目标跟踪 trackObjectNative(long,Bitmap)->float[]
com/android/algo/autoAdjust/AIAutoAdjust.java 自动调色 get3DLutPixel, load2DTexture/load3DTexture(int,int,byte[]), lockBitmapPixels(Bitmap)->long
com/android/videostabilize/VideoStabilize.java 视频稳定 nativeRenderSmooth(long,int×5,float[],float)
com/android/audiosilencedt/AudioSilenceDt.java 静音检测
com/android/autobeat/algo/AutoBeatAlgo.java 自动卡点

消费门面:Yd/c.java→InPaint、p136ge/b.java→Caption、p182j4/h.java→VidSeg、G5/b.java→VidSot、Pc/g.java→AIAutoAdjust、p065d2/c.java→AutoBeatAlgo、p035bc/b.java/p257n2/c.java→VideoStabilize。

4.3 动态加载器三件套

设计模式:门面 over JNI(@Keep + long 句柄)、原生场景图 + Java 侧 GL 管线(per-thread LRU FBO 缓存)、模板方法层体系、单例加载器(ReLinker/NativeLoader)、两种 JNI 风格(GL id/指针型 vs Bitmap 型 + 授权校验)。


5. 人像分割/抠图:本地 MNN + 云端 PreciseCutout 双引擎

5.1 MNN 推理封装

com/android/algo/portraitmatting/PortraitMatting.java(@Keep,extends BaseLibrary):

private static native long initialize(Context c, String segModelPath, String mattingModelPath,
    int segInputSize, int mattingInputSize, boolean needMatting, boolean lowPrecision,
    String publicKey, String[] sha1, String pkg, String sign);
private static native void release(long handle);
private static native int run(long handle, Bitmap src, Bitmap mask /*ALPHA_8 输出*/, float ratioThresh);

5.2 管线与编辑器集成

// 擦除: gl_FragColor = textureColor * textureColor2.a
// eraserType==1: textureColor*(0.65+textureColor2.a*0.35)
// 描边: gl_FragColor = texture2*(1.0-srcColor.a) + srcColor

设计模式:原生门面(BaseLibrary + @Keep + CerChecker 反篡改)、异步(RxJava Callable + Kotlin 协程 + 线程池 + AtomicInteger 进度)、监听器回调(InterfaceC3843gR4/o 任务状态、n.d 模型下载)、DCL 单例、蒙版 = 文件路径 + 版本计数(缓存失效策略)。


6. 画笔/涂鸦/粒子/贴纸

6.1 涂鸦(策略 + 双缓冲 + 命令撤销)

com/camerasideas/instashot/widget/doodle/(18 文件):

文件 原名 职责
DoodleControlView.java DoodleControlView 控制器(787 行:触控路由、双缓冲、undo/redo)
i.java DoodleDrawDelegate 笔迹委托(undo/redo 栈、重画)
l.java IDoodle 画笔策略接口
b.java BaseDoodle 基础画笔(Path + PointF 记录)
c.java BaseWithBitmapDoodle 贴图画笔(沿路径盖章)
j.java DoodleFactory 画笔工厂
DoodleDrawView.java / DoodlePaintView.java 结果 ImageView / 笔刷光标预览
BaseDoodleDrawPathData.java 笔画数据(Parcelable + Gson)
WriteDrawPathData/WaterDrawPathData/PlushDrawPathData.java 钢笔/水笔/荧光笔数据
a/d/k/n/o/p/q/r.java Arrow/Dash/Eraser/Neon/NormalLine/Plush/Water/Write 8 种画笔策略
public final void a() { this.f1429a.drawColor(0, PorterDuff.Mode.CLEAR); }        // 清屏
public final void e(Path path, Paint paint) { this.f1429a.drawPath(path, paint); } // 画一笔
public final void h() { ... if (this.f1432d) { this.f1430b.recycle(); ... } }      // 释放

DoodleControlView.j()committed 层(f32777d)+ working 层(f32778e,拥有并回收 Bitmap),尺寸 = 裁剪区域。

// BaseDoodle.A1()
this.f32755e.quadTo(x2, y2, (x2+x)/2f, (y2+y)/2f);
this.f32761l.add(new PointF(x, y));
c.e(this.f32755e, this.f32754d);           // 画到 CanvasWrapper

Paint:STROKE + ROUND cap/join,宽度 dp((width/500dp)*strokeWidth/scale);橡皮 PorterDuffXfermode(PorterDuff.Mode.CLEAR) + alpha 0;Plush/Water 用 PathMeasure.getPosTan 沿线盖图章(间距 max(1,w/4)、随机旋转、SRC_ATOP 着色)。

6.2 粒子(纯 Canvas + ValueAnimator,无 GL)

6.3 贴纸

V v10 = new V(contextWrapper);
v10.J0(rect.width()); v10.I0(rect.height()); v10.s1(z);
Uri uriA = M7.d.a(aVar.b(contextWrapper));          // path -> file Uri
if (uriA == null || !v10.L1(uriA)) return;
c1730i.a(v10); c1730i.e(); c1730i.J(v10); v10.f24320O = true;  // 加入管理器+选中+弹入动画

7. 文字编辑与字体加载

7.1 字体加载(带缓存的双通道加载器)

Cf/m0.java = Typefaces(静态 Hashtable<String, Typeface> + 双重检查锁):

if (C0659v.q(str)) {                       // 是文件路径 → 先磁盘
    typefaceCreateFromFile = Typeface.createFromFile(str); }
typefaceCreateFromAsset = Typeface.createFromAsset(context.getAssets(), str);  // 否则 assets
f1425a.put(str, typeface);                 // 按名字缓存

assets 根目录确认 115 个文件,含 Roboto-Medium.ttf(默认)、Roboto-Regular/Thin/Condensed-Regular.ttfAmaticSC-Regular.ttfLobster_1.3.otfAngelFace.otfBEBAS.ttfPermanentMarker.ttfBlackout-2am.ttfBPdotsUnicase.otfCaviarDreams.ttfLuckiestGuy-Regular.ttfdanielbd.ttfAileron.otf 等;另有 res/font/roboto_medium_numbers.ttf;子目录 aniemoji01/ 存动效表情帧。字体数据模型 p165i5/e0.java = StoreFontBean(fontId/title/sourceURL/type;type=1 网络字体用 sourceURL,type=0 本地下载文件);ImageTextFontAdapter 预览经 Cf.m0.a(context, fontName);默认字体 KEY_TEXT_FONT 持久化于 B3/b.java = GraphicPreferences

7.2 渲染栈(Java Spannable 模型 → 原生 JNI 排版引擎)

字段 原名 效果
s TextColorSpan 颜色
t TextCycleColorSpan 循环变色
E TextStrokeSpan 描边
v TextGlowSpan 发光
z TextNeonSpan 霓虹
A TextShadowSpan 阴影
G TextUnderlineShadowSpan 下划线+阴影
F TextUnderlineNeonSpan 下划线+霓虹
H/I TextUnderlineSpan 下划线
x TextLabelSpan 标签
u TextFontSpan 字体
// S5/l.java
LottieTextLayout l = layoutInfo.createLayout();
l.measure(contextD, nVarP, mA, jVar2);        // org.instory.* JNI

org/instory/suit/textLayout/LottieDefaultTextLayout.javaJNI 文字引擎:@Keep private native long nInit(); nMeasure(long, AssetManager, TextInfo, List<LottieTextSpan>, LottieMeasureInfo); nRelease(long);变体 LottieBendTextLayout / LottieHeartTextLayout / LottieBezierCurveTextLayout / LottieTextFitInPathLayout。字号换算 graphics/text/C1753g.java = FontScaleResolver:baseFontSize * (canvasWidth/1080) * 9.642858 * scale;配套 graphics/text/ 包还有 ColorStyle/C1750d DropShadow/C1755i LinearGradientInfo/C1754h LabelImageInfo/C1756j MeasuredTextInfo/TextAttributesTypeAdapterFactory 等 30+ 类。


8. 图像导出:JPEG(TurboJPEG)/PNG,WebP 仅解码

8.1 JPEG 编码(libisTurboJpeg.so)

加载点 Bc/f.java = TJLoader:System.loadLibrary("isTurboJpeg")(带计时日志);JNI 桥 com/inshot/compressor/TJCompressor.java:

public static boolean a(String str, Bitmap bitmap, int i) { return nCompress(bitmap, i, str.getBytes()); }
public static boolean b(int i, int i10, Bitmap bitmap)     { return nCompressFd(bitmap, i10, i); }  // MediaStore fd
private static native boolean nCompress(Bitmap, int, byte[]);
private static native boolean nCompressFd(Bitmap, int, int);
private static native boolean nDecryptDecompress(Bitmap, byte[]);
private static native boolean nEncryptCompress(Bitmap, int, byte[]);   // 加密导出变体

Bc/e.java = TJJpegCompressor:先把任意 config 归一为 ARGB_8888(API≥26 保留 ColorSpace)再交给 native;Bc/a.java = Compressor 抽象类即格式策略分发器:PNG 或广色域 ColorSpace → Bc/b.java = JavaCompressor(Bitmap.compress),否则 TurboJPEG(API≥29 优先 MediaStore openFileDescriptor("w") 的 fd 编码),native 失败再回退 Java,每步计时打日志。

8.2 WebP:仅解码

全树 grep 无导出级 WebP 编码器;G2/m.java = WebpDrawableEncoder 只是 Fresco 资源缓存层(把 drawable 的 ByteBuffer 写文件);libstatic-webp 由 Fresco com/facebook/imagepipeline/nativecode/c.java = StaticWebpNativeLoaderQ8.a.b("static-webp") 加载,com/bumptech/glide/integration/webp/(WebpBitmapFactory/WebpImage/WebpFrame/WebpHeaderParser)也是纯解码。图片导出 = JPEG 或 PNG

8.3 完整保存管线(离屏 GL 渲染 → 黑屏检测 → 编码 → MediaStore)

x0Var = new x0(EGL10.EGL_NO_CONTEXT, w, h, colorSpace);   // PixelBuffer 离屏
x0Var.c(b10);                                             // renderer(T3/l ImageSaveRenderer 或 T3/B StitchSaveRenderer)
Bitmap bitmapB = x0Var.b();                               // glReadPixels → Bitmap
if (bitmapB != null && this.f10685c.a(bitmapB) && !z10) return false;  // BlackDetect
z11 = Bc.a.a(context2, bitmapB, path, srgbOrdinal, 90, isPng);         // 编码

8.4 裁剪

bVar.f60393a = actualCropRect.left   / this.f23977f.width();   // left  0..1
bVar.f60394b = actualCropRect.top    / this.f23977f.height();  // top
bVar.f60395c = actualCropRect.right  / this.f23977f.width();   // right
bVar.f60396d = actualCropRect.bottom / this.f23977f.height();  // bottom

9. 总体架构结论(设计模式清单)

  1. MVP + 事件总线:每个编辑功能 = Fragment(View 接口)+ Presenter(继承 BaseImagePresenter/BaseEditPresenter 链),Presenter 经 IImageEditView 接口回调 UI,跨层通知走 GreenRobot EventBus(@nj.k = Subscribe)。
  2. 自研 "GraphicsDrawable" 画布模型(graphicproc 97 类):BaseItem 条目体系 + GraphicItemManager 单例 + GtrRenderer/GtrDrawable 合成 + Gson(@SerializedName 稳定键)草稿序列化 + 12 类渲染异常——与 InShot 开源 GraphicsDrawable 同构。
  3. 命令 + 备忘录撤销重做:BackForward 四栈 + OpData(EditInfo 深拷贝快照)+ OpDataRollBack 回滚 + 监听器/EventBus 通知。
  4. 复合/责任链:GPUImageFilterGroup 树 + ping-pong FBO 流水线;GPUImageEditorFilter = 调整链(GPUFilterChainGroup)+ 特效链。
  5. 工厂 + 策略:特效工厂(C3562u 反射)、3D/2D LUT 代理(Pc/i)、导出格式/尺寸策略(Bc/a、T3/x)、画笔工厂(DoodleFactory)、文字版式工厂(T5/g TextLayoutFactory)。
  6. 原生门面 + 句柄对象:SkiaImageEngine(long 句柄 + GL id)、BaseLibrary(Bitmap 型 JNI + CerChecker 授权)、ReLinker/NativeLoader 双加载器;重活(排版/抠图/马赛克/编解码)全部下沉 native(org.instory.*libImageKit.solibYXAImg.solibisTurboJpeg.solibMNN.so)。
  7. 缓存/复用:GLProgramCache(ThreadLocal)、FrameBufferCache(LRU,per-thread)、Typefaces(Hashtable)、LookupBitmap(SoftReference + inBitmap)、贴图内存缓存 p307pe.j。
  8. 防御式错误处理:BlackImageException/GLOutOfMemoryError/WrongFilterSizeException 等分类异常 + 降级策略(native→Java 编码、尺寸阶梯)。

关键文件速查

主题 文件
编辑壳 instashot/ImageEditActivity.javainstashot/AbstractEditActivity.javainstashot/widget/ImageEditLayoutView.javainstashot/widget/EditMenu.java
状态/撤销 p088e6/C3773e0.java(ImageEditPresenter)、Y5/b.java(BaseEditPresenter)、p126g4/c.java(BackForward)、p126g4/k.java(OpData)、p126g4/p.java(PhotoOpDataCreator)、instashot/common/C1773e0.java(EditInfo)
画布框架 graphicproc/graphicsitems/{ItemView,C1730i,AbstractC1723b,C1731j,C1733l,V,X,K,C,C1734m,C1740t}.javacom/camerasideas/graphics/entity/a.java(BaseClipInfo)、p088e6/C3781h.java(GLGraphicsContext)、Oh/a.java(FrameBufferRenderer)、N3/(GtrDrawable)、E3/a.java(GsonBuilderFactory)
滤镜 com/inshot/graphics/extension/{Q,S,U,C3582z,C3562u,K3}.javaU1/{a,b,d}.javaPc/i.javaI4/{N,O}.javafragment/image/ImageFilterFragment.javap088e6/C3829x0.java(ImageFilterPresenter)、resources/assets/GPU3DLookupFilter.glslresources/assets/GPUAdjustLookupFilter.glsl
原生库 com/inshot/graphics/engine/SkiaImageEngine.javacom/inshot/graphics/layer/*.javacom/android/algo/base/b.java(BaseLibrary)、com/android/algo/portraitmatting/{PortraitMatting,Contours,PortraitMattingParam}.javacom/inshot/compressor/TJCompressor.javaV8/c.java(ReLinker)、Q8/a.java(NativeLoader)
抠图 graphicproc/utils/{c,q,n}.javap156he/k.java(PortraitSegment)、p088e6/{N,U0,C3826w0}.javaR4/e.java(AiMattingManager)、fragment/image/{ImageCutoutFragment,ImageEraserFragment,ImageOutlineFragment}.javagraphicproc/entity/OutlineProperty.javacom/inshot/graphics/layer/u.java(MattingLayer)
涂鸦/贴纸/文字 instashot/widget/doodle/instashot/widget/particle/M5/N5/graphicproc/graphicsitems/V.java(StickerItem)、F3/C.java(ItemStickerHelper)、Cf/m0.java(Typefaces)、com/camerasideas/graphics/text/p.java(TextAttributes)、S5/{l,q}.javaT5/g.javaorg/instory/suit/textLayout/*.javafragment/image/ImageTextFragment.javap088e6/C3810q1.java(ImageTextPresenter)
导出 T3/{k,n,l,B,x,v,z,C1050c,q}.javainstashot/save/c.java(ItemParamBuilder)、Bc/{a,b,e,f,g}.javacom/camerasideas/crop/CropImageView.javainstashot/ImageResultActivity.javap333r3/b.javaUc/c.java