在安卓項目中使用gifsicle編輯GIF動圖-Android NDK 編譯 gifsicle 為可執(zhí)行文件記錄

一褪测、前言

最近項目中有需要壓縮GIF的需求,最開始時試圖使用FFmpeg通過降低GIF的分辨率和幀率的來減少GIF文件體積,但實際測試下來涵卵,大多數(shù)情況下壓縮效果并不理想,甚至會出現(xiàn)降低分辨率后導出的GIF甚至比原文件還大的情況荒叼。
故選擇放棄FFmpeg轿偎,經(jīng)過大量的查詢資料,發(fā)現(xiàn)如果想要壓縮GIF大致有以下幾個途徑:

參考文章

1.由于 GIF 支持全局調(diào)色盤和局部調(diào)色盤被廓,在沒有局部調(diào)色盤的時候會用放在文件頭中的全局調(diào)色盤坏晦。所以對于顏色變化不大的 GIF,可以將顏色放入全局調(diào)色盤中嫁乘,去除局部調(diào)色盤昆婿。

  1. 對于顏色較少的 GIF,將調(diào)色盤大小減少蜓斧,比如從 256 種減少到 128 種等仓蛆。
    3.對于背景一致,畫面中有一部分元素在變化的 GIF挎春,可以將多個元素和背景分開存儲看疙,然后加上如何還原的信息
    4.對于背景一致,畫面中有一部分元素在動的 GIF直奋,可以和前面一幀比較狼荞,將不動的部分透明化
    5.對于幀數(shù)很多的 GIF,可以抽取中間部分的幀帮碰,減少幀數(shù)
    6.對于每幀分辨率很高的 GIF相味,將每幀的分辨率減小

而正如前文所述,抽幀(減小幀率)和減少分辨率有時候效果并不是很好殉挽,而且對于圖片的質(zhì)量損耗較大丰涉。
對于1拓巧,2條途徑依然可以使用FFmpeg實現(xiàn),但是效果也不理想一死,并且處理起來比較復雜肛度。故剩余的處理方式只剩3和4了。但是正如上文作者所說:“在移動端投慈,除非將 ImageMagick 或者 gifsicle 移植到 iOS&Android 上承耿,要實現(xiàn)前面 4 個方法是比較困難的∥泵海”
作者提到了兩個程序:ImageMagick和gifsicle加袋,經(jīng)過查詢ImageMagick已支持安卓,gifsicle尚未支持抱既。但是ImageMagick目前對于安卓的支持較差职烧,限制較多:

Requires API >= 24 (>= Nougat)
Currently, only arm64-v8a is supported

并且其庫過于龐大:

1

對于我來說只需要它的壓縮動圖功能,卻需要添加這么大的庫防泵,性價比過低蚀之。
不過如果讀者有需要的可以試試,項目地址:ImageMagick
也就是說捷泞,現(xiàn)在對于我來說只剩下gifsicle可用足删,但是gifsicle尚未提供安卓可用版本。
gifsicle項目地址:gifsicle
如何編譯 gifsicle 使其在安卓上可用便是本文想要探討的問題锁右。

二失受、gifsicle編譯方案

原計劃是在gifsicle之上使用jni封裝使其可以在安卓中調(diào)用其接口,但是通過對gifsicle的源碼以及issus研究骡湖,發(fā)現(xiàn)該項目只支持直接編譯成可執(zhí)行文件贱纠,且修改較為困難。這也是為什么至今沒有移植到安卓上的原因响蕴。
github中關于將gifsicle移植成庫的討論
雖說不能直接移植成安卓的 .so 庫谆焊,但是即使是編譯成可執(zhí)行文件也可以通過

Runtime.getRuntime().exec(cmd, envp)

使用該庫,唯一需要注意的是浦夷,在安卓10中可能會禁止執(zhí)行外部可執(zhí)行庫:

Android 10 includes the following security changes.
Removed execute permission for app home directory
Untrusted apps that target Android 10 cannot invoke exec() on files within the app's home directory. This execution of files from the writable app home directory is a W^X violation. Apps should load only the binary code that's embedded within an app's APK file.
In addition, apps that target Android 10 cannot in-memory modify executable code from files which have been opened with dlopen(). This includes any shared object (.so) files with text relocations.

不過經(jīng)過實際測試辖试,只要是打包進apk中且命名形如 libxxx.so 的可執(zhí)行文件依舊可以使用。
確定好如何編譯后下面就開始編譯

三劈狐、開始編譯

1.編譯前準備

參考:
想請教關于gifsicle在android上編譯的問題
seven332/gifsicle
1.編譯環(huán)境
我使用的是WSL2 Linux+NDK r21b
Linux版本如下(因為電腦上正好裝著Kali所以就用Kali了罐孝,一般用Ubuntu就行)

2

2.安裝依賴
因為編譯前需要 automake 生成 config.h 所以需要安裝以下依賴(已安裝請忽略)

sudo apt-get install autoconf automake libtool
sudo apt-get install libffi-dev

2.下載代碼
編譯前首先將 gifsicle 下載下來
這里直接 clone 官方倉庫:

git clone https://github.com/kohler/gifsicle.git

切進代碼目錄

cd gifsicle

3.生成 config.h


3

依據(jù)官方文檔,首先生成 config

autoreconf -i

根據(jù)需要執(zhí)行 configure 肥缔,因為我只需要壓縮gif功能莲兢,所以其他模塊就不需要編譯了:

./configure --disable-gifview --disable-gifdiff

此時目錄中應該已經(jīng)生成了一個 config.h 文件。
==切記不要執(zhí)行 make 和 install==

4.編寫 Android.mk 文件
在代碼根目錄中新建一個 Android.mk 文件,內(nèi)容如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := gifsicle
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_SRC_FILES := \
src/clp.c  \
src/fmalloc.c   \
src/giffunc.c  \
src/gifread.c   \
src/gifsicle.c  \
src/gifunopt.c   \
src/gifwrite.c  \
src/merge.c   \
src/optimize.c   \
src/quantize.c   \
src/support.c   \
src/xform.c  

LOCAL_CFLAGS := -DHAVE_CONFIG_H
 
include $(BUILD_EXECUTABLE)

再新建一個 Application.mk 文件改艇,內(nèi)容如下:

APP_ABI := all

APP_PLATFORM := android-16

NDK_TOOLCHAIN_VERSION := clang

其他東西不需理會收班,記住 APP_ABI := all 表示需要編譯的CPU ABI 版本即可,這里寫的是所有版本都編譯一份谒兄,你也可以指定只編譯特定的版本摔桦,如 APP_ABI := armeabi-v7a

5.準備編譯
將代碼目錄更名為 jni
否則ndk不會將該項目識別為ndk項目并且編譯。
做完上述步驟后的完整目錄結構如下:

4

2.開始編譯

1.首先確保你的ndk已經(jīng)安裝并且已配置環(huán)境承疲,否則將會

-bash: ndk-build: command not found

安裝ndk并配置環(huán)境可參考:
VMware安裝Ubuntu教程邻耕,Linux下搭建Android開發(fā)環(huán)境
中關于NDK的介紹(AS,SDK等不需要裝)

2.切換至 jni 文件夾的上層目錄:

cd ../

如圖:
8

3.編譯64位的so(arm64-v8a, x86_64)
首先修改 Application.mk 文件的 APP_ABI 為 arm64-v8a, x86_64

vim ./jni/Application.mk

修改后如圖:


99

修改后執(zhí)行:

ndk-build

如圖即為編譯成功:


8976545

生成的so庫在 ./libs 目錄下


34667

4.編譯32位so(如:armeabi-v7a)
如3中所說燕鸽,首先修改 Application.mk 文件的 APP_ABI 為 armeabi-v7a
4565

修改 ./jni/config.h 文件

vim ./jni/config.h

找到

#define SIZEOF_UNSIGNED_LONG 8

修改為

#define SIZEOF_UNSIGNED_LONG 4

如圖:

123

切記一定要修改不然會報錯如下
1312

之后執(zhí)行

ndk-build

即可兄世。

注意: 上述編譯32位時如果不修改 ./jni/config.h 會出現(xiàn)報錯的情況,根據(jù)我猜測應該時由于我使用的是 Linux 的 automake 工具生成的 config.h 文件绵咱,也就是說這個config.h 文件是適用于編譯成我使用 automake 的系統(tǒng)(kali)的配置文件碘饼,而非用于我生成安卓可執(zhí)行文件的配置文件熙兔。
根據(jù)報錯信息找到源碼出錯地方如下:

53535

根據(jù)源碼中注釋的描述悲伶,此處代碼是用于檢測 Windos 下編譯腳本是否出錯的問題(即是否在 64位 系統(tǒng)使用了 32位 腳本進行編譯或者反之)
根據(jù)源碼來說,此處并未有除檢測外實際作用住涉,故我直接根據(jù)指示將 config.h 中的 UNSIGNED_LONG 值設置為4來規(guī)避檢測麸锉。
雖然樣能夠編譯成功,初步測試使用也沒有問題舆声,但是我也不確定是否會影響到 gifsicle 其他功能的使用花沉。
所以讀者如有需要使用,還需自行測試是否存在問題媳握,如果有人知道其中的問題也希望能不吝賜教碱屁,十分感謝。

四蛾找、使用方法

1.復制庫

將編譯成功的庫復制到您的安卓項目文件

項目根目錄\app\src\main\jniLibs

下娩脾,并且改名為 libgifsicle.so

在這里插入圖片描述

一定要記得改名,否則安裝時不會被系統(tǒng)復制至可執(zhí)行文件目錄下打毛。

2.使用

代碼中已用注釋說明各個語句的作用柿赊。

val gifsicle = File(File(applicationInfo.nativeLibraryDir), "libgifsicle.so")   //可執(zhí)行文件地址安裝后形如:/data/app/com.equationl.myapplication-wZxpZo7IgVPNv3jvY0S8QA==/lib/arm/libgifsicle.so
if (!gifsicle.canExecute()) {   //無法執(zhí)行該執(zhí)行文件
    Log.e("el", "startCustomizeCompress: can't excute")
}

val envp = arrayOf("LD_LIBRARY_PATH=" + File(applicationInfo.nativeLibraryDir))  //設置環(huán)境
val cmd = String.format(Locale.US, "%s -i %s -k 256 -O3 -o %s",
        gifsicle.path, File(externalCacheDir, "test.gif").toString(), File(externalCacheDir, "result.gif").toString())  //設置命令,此處作用為將緩存目錄下的 test.gif 更改顏色數(shù)為256 按第3級別優(yōu)化并輸出至緩存目錄下 result.gif (詳細請自己看 gifsicle 的文檔)
Log.i("el", "startCustomizeCompress: envp=${envp[0]}\ncmd=$cmd")
val process = Runtime.getRuntime().exec(cmd, envp) //開始執(zhí)行命令
try {
    if (process.waitFor() != 0) {  //如果執(zhí)行成功會返回 0幻枉,不成功返回非0
        Log.e("el", "startCustomizeCompress: running error process.waitFor() != 0")
    }
    else {
        Log.i("el", "Success!")
    }
} catch (e: InterruptedException) {
    e.printStackTrace()
}

關于 gifsicle 的使用方法請自行查看官方文檔

@equationl 原創(chuàng)
@email:equationl@qq.com

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末碰声,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子熬甫,更是在濱河造成了極大的恐慌胰挑,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瞻颂,居然都是意外死亡脚粟,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門蘸朋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來核无,“玉大人,你說我怎么就攤上這事藕坯⊥拍希” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵炼彪,是天一觀的道長吐根。 經(jīng)常有香客問我,道長辐马,這世上最難降的妖魔是什么拷橘? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮喜爷,結果婚禮上冗疮,老公的妹妹穿的比我還像新娘。我一直安慰自己檩帐,他們只是感情好术幔,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著湃密,像睡著了一般诅挑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泛源,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天拔妥,我揣著相機與錄音,去河邊找鬼达箍。 笑死没龙,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的幻梯。 我是一名探鬼主播兜畸,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼碘梢!你這毒婦竟也來了咬摇?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤煞躬,失蹤者是張志新(化名)和其女友劉穎肛鹏,沒想到半個月后逸邦,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡在扰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年缕减,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芒珠。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡桥狡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出皱卓,到底是詐尸還是另有隱情裹芝,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布娜汁,位于F島的核電站嫂易,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏掐禁。R本人自食惡果不足惜怜械,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望傅事。 院中可真熱鬧缕允,春花似錦、人聲如沸享完。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽般又。三九已至,卻和暖如春巍佑,著一層夾襖步出監(jiān)牢的瞬間茴迁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工萤衰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留堕义,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓脆栋,卻偏偏與公主長得像倦卖,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子椿争,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容