此答案扩展了 Alex Cohn 的答案。完全归功于他让我开始。这个答案将允许您使用 ARM(带或不带 NEON)以及 x86 的程序集构建 libjpeg-turbo。还为 MIPS 提供了兼容性,这样您就可以成为一个好公民,并为 Android NDK 的 r8e 版本支持的所有平台构建您的应用程序。
对于实际回答问题,我仍然是一个相对新手,所以没有包含附件的权限。所以我将不得不内联一堆代码。我也确信有更好的方法来做下面的一些事情,所以我愿意接受改进的建议。
通过六个主要步骤完成这一切(第二步有许多子步骤——每个步骤都可以在主要六个步骤之后找到):
-
通过 git 下载 libjpeg-turbo 库,其中包含一组适用于 Android 的代码和 .mk 文件。我用 git 从这里下载了它:
git clone git://git.linaro.org/people/tomgall/libjpeg-turbo/libjpeg-turbo.git -b linaro-android
必须感谢 Tom Gall 完成了最初的工作。
-
下载 YASM(x86 的汇编程序)并配置 Android 构建环境以支持它。请参阅关于为 Android NDK 配置 YASM 的说明集,我在步骤 6 中我的 Android.mk 更改末尾的水平线下方包含了这些说明。支持 x86 要求您不要羞于调整 NDK 安装。
我或许本可以使用 NASM 编译器,但在调查 NASM 之前,我已经完成了 YASM 路径。
-
创建 simd/jsmidcfg.inc 文件。 libjpeg-turbo 库提供 simd/jsmidcfg.inc.h。它旨在让预处理器获取 .h 并将其转换为 .inc。我确信有比我所做的更好的方法来实现它。但由于时间紧迫,未能整理好。相反,我使用以下命令手动运行 Android 附带的 gcc 来创建文件:
C:\development\android\ide\android-ndk-r8e-windows-x86_64\toolchains\x86-4.4.3\prebuilt\windows-x86_64\bin\i686-linux-android-gcc.exe -E C:\development\thirdparty\libjpeg-turbo\libjpeg-turbo-android-1.2.0\simd\jsimdcfg.inc.h -o C:\development\thirdparty\libjpeg-turbo\libjpeg-turbo-android1.2.0\simd\jsimdcfg.inc
预处理器完成后,我必须手动编辑文件以使其进入可用状态。我必须选择“; -- jpeglib.h”上面的所有行并删除它们。
接下来,我必须找到以 _cpp_protection_ 开头的每个 %define,并复制每个没有 _cpp_protection_ 的 %define。例如,%define _cpp_protection_RGB_RED 0 变为 %define RGB_RED 0
-
对于 ARM,我希望我的代码能够在具有它的硬件上动态支持 NEON 指令,并在没有它的硬件上回退到更简单的东西,我修改了 simd/jsimd_arm.c 以动态查询信息。我不想在构建 libjpeg-turbo 时做出这样的选择。
我添加了以下代码行:
#ifdef ANDROID
#include <cpufeatures/cpu-features.h>
#endif
和
#ifdef ANDROID
uint64_t cpuFeatures = android_getCpuFeatures();
if (cpuFeatures & ANDROID_CPU_ARM_FEATURE_NEON) {
simd_support |= JSIMD_ARM_NEON;
}
#endif
在 init_simd() 方法中。
-
启用 MIPS。
作为一个好公民,我想在 MIPS 上启用编译。虽然 MIPS 没有任何汇编代码,但代码至少应该编译和运行。为此,我将 simd/jsimd_arm.c 复制到 simd/jsimd_mips.c。我编辑了文件,以便 init_simd() 设置 simd_support = 0; 我还更改了所有 jsimd_can_*() 方法以返回 0。最后,我从所有其他方法中删除了实现。
-
因为我不仅对 ARM 的构建感兴趣,因此我更改了 Application.mk 文件以包含以下几行:
APP_ABI := all
APP_OPTIM := release
APP_OPTIM 来自How to optimize a native code with android-ndk (Speed Optimization)
在 Android.mk 中,我注释掉了“cjpeg”注释块及下方的所有内容。做一个块评论,我听从了How to add multi line comments in makefiles的建议
接下来我自定义了 Android.mk 文件本身,以便我可以为所有当前支持的 CPU(ARM、x86、MIPS)构建。这是我最终得到的结果(一些代码被注释掉了,因为我想要一个静态库——也将原始代码留在原处,这样我就可以看到发生了什么变化)。您很可能必须更改(或删除) addprefix 方法调用,因为下面的文件是为我的构建环境定制的。但除此之外,它应该对你有用。
.
##################################################
### simd ###
##################################################
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#ifeq ($(ARCH_ARM_HAVE_NEON),true)
#LOCAL_CFLAGS += -D__ARM_HAVE_NEON
#endif
# From autoconf-generated Makefile
EXTRA_DIST = simd/nasm_lt.sh simd/jcclrmmx.asm simd/jcclrss2.asm simd/jdclrmmx.asm simd/jdclrss2.asm \
simd/jdmrgmmx.asm simd/jdmrgss2.asm simd/jcclrss2-64.asm simd/jdclrss2-64.asm \
simd/jdmrgss2-64.asm simd/CMakeLists.txt
#libsimd_SOURCES_DIST = simd/jsimd_arm_neon.S \
simd/jsimd_arm.c
libsimd_SOURCES_DIST :=
ifeq ($(TARGET_ARCH),arm)
libsimd_SOURCES_DIST := simd/jsimd_arm_neon.S \
simd/jsimd_arm.c
endif
ifeq ($(TARGET_ARCH),x86)
#libsimd_SOURCES_DIST := simd/jsimd_mips.c
# List of assembly files needed is derived from content within simd/CMakelists.txt
# The Intel Atom supports x86 32-bit assembly. So take those files and leave the
# 64-bit behind.
libsimd_SOURCES_DIST := simd/jsimd_i386.c \
simd/jsimdcpu.asm simd/jccolmmx.asm simd/jcgrammx.asm simd/jdcolmmx.asm simd/jcsammmx.asm simd/jdsammmx.asm \
simd/jdmermmx.asm simd/jcqntmmx.asm simd/jfmmxfst.asm simd/jfmmxint.asm simd/jimmxred.asm simd/jimmxint.asm simd/jimmxfst.asm simd/jcqnt3dn.asm \
simd/jf3dnflt.asm simd/ji3dnflt.asm simd/jcqntsse.asm simd/jfsseflt.asm simd/jisseflt.asm simd/jccolss2.asm simd/jcgrass2.asm simd/jdcolss2.asm \
simd/jcsamss2.asm simd/jdsamss2.asm simd/jdmerss2.asm simd/jcqnts2i.asm simd/jfss2fst.asm simd/jfss2int.asm simd/jiss2red.asm simd/jiss2int.asm \
simd/jiss2fst.asm simd/jcqnts2f.asm simd/jiss2flt.asm
endif
ifeq ($(TARGET_ARCH),mips)
libsimd_SOURCES_DIST := simd/jsimd_mips.c
endif
#LOCAL_SRC_FILES := $(libsimd_SOURCES_DIST)
#LOCAL_SRC_FILES := $(addprefix ../../libjpeg-turbo-android-1.2.0/,$(LOCAL_SRC_FILES))
LOCAL_C_INCLUDES := $(LOCAL_PATH)/simd \
$(LOCAL_PATH)/android
LOCAL_C_INCLUDES := simd android
#LOCAL_C_INCLUDES := $(addprefix ../../libjpeg-turbo-android-1.2.0/,$(LOCAL_C_INCLUDES))
#AM_CFLAGS := -march=armv7-a -mfpu=neon
#AM_CCASFLAGS := -march=armv7-a -mfpu=neon
#LOCAL_MODULE_TAGS := debug
#LOCAL_MODULE := libsimd
#include $(BUILD_STATIC_LIBRARY)
######################################################
### libjpeg.so ##
######################################################
#include $(CLEAR_VARS)
# From autoconf-generated Makefile
libjpeg_SOURCES_DIST = jcapimin.c jcapistd.c jccoefct.c jccolor.c \
jcdctmgr.c jchuff.c jcinit.c jcmainct.c jcmarker.c jcmaster.c \
jcomapi.c jcparam.c jcphuff.c jcprepct.c jcsample.c jctrans.c \
jdapimin.c jdapistd.c jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c \
jddctmgr.c jdhuff.c jdinput.c jdmainct.c jdmarker.c jdmaster.c \
jdmerge.c jdphuff.c jdpostct.c jdsample.c jdtrans.c jerror.c \
jfdctflt.c jfdctfst.c jfdctint.c jidctflt.c jidctfst.c jidctint.c \
jidctred.c jquant1.c jquant2.c jutils.c jmemmgr.c jmemnobs.c \
jaricom.c jcarith.c jdarith.c \
turbojpeg.c transupp.c jdatadst-tj.c jdatasrc-tj.c #\
turbojpeg-mapfile
LOCAL_SRC_FILES:= $(libjpeg_SOURCES_DIST) $(libsimd_SOURCES_DIST)
#LOCAL_SRC_FILES:= $(libjpeg_SOURCES_DIST)
LOCAL_SRC_FILES := $(addprefix ../../../libjpeg-turbo-android-1.2.0/,$(LOCAL_SRC_FILES))
#LOCAL_SHARED_LIBRARIES := libcutils
#LOCAL_STATIC_LIBRARIES := libsimd
#LOCAL_C_INCLUDES := $(LOCAL_PATH)
# Include C:/development/android/ide/android-ndk-r8e-windows-x86_64/sources/android
# instead of the lower-level cpufeatures because of how I had to include
# cpu-features.h. It appears as if there is a naming conflict, so I had to
# change how the file was included to avoid naming conflicts.
LOCAL_C_INCLUDES := $(addprefix ../../../libjpeg-turbo-android-1.2.0/,$(LOCAL_C_INCLUDES)) \
C:/development/thirdparty/libjpeg-turbo/libjpeg-turbo-android-1.2.0 \
C:/development/android/ide/android-ndk-r8e-windows-x86_64/sources/android
#LOCAL_C_INCLUDES := $(LOCAL_C_INCLUDES)) ./
#LOCAL_C_INCLUDES := $(addprefix $(LOCAL_PATH),$(LOCAL_C_INCLUDES)) $(LOCAL_PATH)../../../libjpeg-turbo-android-1.2.0/
LOCAL_CFLAGS := -DAVOID_TABLES -O3 -fstrict-aliasing -fprefetch-loop-arrays -DANDROID \
-DANDROID_TILE_BASED_DECODE -DENABLE_ANDROID_NULL_CONVERT
LOCAL_CFLAGS += -DJPEG_LIB_VERSION=80 # I need version 8 because of some of the methods I depend on
LOCAL_YASMFLAGS := -P../../libjpeg-turbo-android-1.2.0/simd/jsimdcfg.inc
#LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_STATIC_LIBRARY)
#LOCAL_MODULE_TAGS := debug
#LOCAL_MODULE := libjpeg
LOCAL_MODULE := jpeg-turbo
#include $(BUILD_SHARED_LIBRARY)
#LOCAL_STATIC_LIBRARIES := cpufeatures
include $(BUILD_STATIC_LIBRARY)
# We reference android/cpufeatures in the Android.mk file of our main app, so
# we don't need to do anything here.
#$(call import-module,android/cpufeatures)
libjpeg-turbo 附带的一些源代码采用 .asm 格式。也就是说,它需要使用 NASM 汇编器进行编译。在发布时,Android NDK 不支持它。
为了让 .asm 支持与 NDK 一起使用,我遵循了此处提供的粗略大纲:http://software.intel.com/en-us/articles/using-yasm-compiler-on-android-ndkbuild
这里有一组更详细的说明。在 android-ndk-r8\build\core 中,需要发生以下事情:
-
build-binary.mk:
all_source_extensions := .c .s .S $(LOCAL_CPP_EXTENSION) => all_source_extensions := .c .s .asm .S $(LOCAL_CPP_EXTENSION)
对我来说,上面是第 228 行。
在以“# Build the sources to object files”开头的行之后,您将看到几个 foreach 循环。像这样添加一个(我在第 272 行添加了它):
$(foreach src,$(filter %.asm,$(LOCAL_SRC_FILES)), $(call compile-asm-source,$(src),$(call get-object-name,$(src))))
-
定义.mk:
创建一个新的 compile-asm-source 以匹配我们在上面的新 for 循环中添加的新条目。我们还必须在 make 文件中添加一个新的定义。我添加了以下内容。它是用于构建 .c 文件的各种规则的组合和简化。使用 yasm 时,我们需要一组不同的选项来进行这些更改。
define ev-compile-asm-source
_SRC:=$$(LOCAL_PATH)/$(1)
_OBJ:=$$(LOCAL_OBJS_DIR)/$(2)
_FLAGS := -f elf -DELF -DPIC $$(LOCAL_YASMFLAGS)
_TEXT := "Compile $$(call get-src-file-text,$1)"
_CC := $$(NDK_CCACHE) $$(TARGET_ASM)
$$(_OBJ): PRIVATE_SRC := $$(_SRC)
$$(_OBJ): PRIVATE_OBJ := $$(_OBJ)
$$(_OBJ): PRIVATE_DEPS := $$(call host-path,$$(_OBJ).d)
$$(_OBJ): PRIVATE_MODULE := $$(LOCAL_MODULE)
$$(_OBJ): PRIVATE_TEXT := "$$(_TEXT)"
$$(_OBJ): PRIVATE_CC := $$(_CC)
$$(_OBJ): PRIVATE_CFLAGS := $$(_FLAGS)
ifeq ($$(LOCAL_SHORT_COMMANDS),true)
_OPTIONS_LISTFILE := $$(_OBJ).cflags
$$(_OBJ): $$(call generate-list-file,$$(_FLAGS),$$(_OPTIONS_LISTFILE))
$$(_OBJ): PRIVATE_CFLAGS := @$$(call host-path,$$(_OPTIONS_LISTFILE))
$$(_OBJ): $$(_OPTIONS_LISTFILE)
endif
$$(call generate-file-dir,$$(_OBJ))
$$(_OBJ): $$(_SRC) $$(LOCAL_MAKEFILE) $$(NDK_APP_APPLICATION_MK) $$(NDK_DEPENDENCIES_CONVERTER)
@$$(HOST_ECHO) "$$(PRIVATE_TEXT) : $$(PRIVATE_MODULE) <= $$(notdir $$(PRIVATE_SRC))"
$$(hide) $$(PRIVATE_CC) $$(PRIVATE_CFLAGS) $$(call host-path,$$(PRIVATE_SRC)) -o $$(call host-path,$$(PRIVATE_OBJ)) \
$$(call cmd-convert-deps,$$(PRIVATE_DEPS))
endef
compile-asm-source = $(eval $(call ev-compile-asm-source,$1,$2))
搜索以“compile-s-source”开头的行。我在第 1491 行找到它。新的 compile-asm-source 可以在它的正下方。
接下来,搜索以“get-object-name”开头的行。我在第 1270 行找到它。将“.asm”添加到内部 for 循环中。将其与 .c、.s 和 .S 放在一起
-
import-locals.mk:
在 LOCAL_CFLAGS 和 LOCAL_CPPFLAGS 行下方添加以下行
LOCAL_YASMFLAGS := $(LOCAL_CFLAGS) $(strip $(LOCAL_YASMFLAGS))
-
默认构建命令.mk:
在文件的任意位置添加以下行。我把我的放在 TARGET_CXX 部分下面和 TARGET_LD 部分上面。
TARGET_ASM = $(TOOLCHAIN_PREFIX)yasm
-
下载 yasm.exe 并将其复制到构建文件夹:
从这里下载一份 YASM:http://yasm.tortall.net/
我有Win64版本的NDK,所以下载了Win64版本的YASM。如果你有 NDS 的 Win32 版本,请下载 YASM 的 Win32 版本。
您应该只获取 .exe。将其作为 yasm.exe 复制到以下目录。如果您有任何其他工具链版本,请将其复制到这些目录:
C:\development\android\ide\android-ndk-r8e-windows-x86_64\toolchains\x86-4.4.3\prebuilt\windows-x86_64\i686-linux-android\bin
C:\development\android\ide\android-ndk-r8e-windows-x86_64\toolchains\x86-4.6\prebuilt\windows-x86_64\i686-linux-android\bin
C:\development\android\ide\android-ndk-r8e-windows-x86_64\toolchains\x86-4.7\prebuilt\windows-x86_64\i686-linux-android\bin
然后,将该文件作为 i686-linux-android-yasm.exe 再次复制到以下目录。如上所述,如果您有任何其他工具链版本,请将其复制到这些目录中:
C:\development\android\ide\android-ndk-r8e-windows-x86_64\toolchains\x86-4.4.3\prebuilt\windows-x86_64\bin
C:\development\android\ide\android-ndk-r8e-windows-x86_64\toolchains\x86-4.6\prebuilt\windows-x86_64\bin
C:\development\android\ide\android-ndk-r8e-windows-x86_64\toolchains\x86-4.7\prebuilt\windows-x86_64\bin