深度解析Android Q recovery字体图片生成流程

build/make/core/Makefile

# Set recovery_density to a density bucket based on TARGET_SCREEN_DENSITY, PRODUCT_AAPT_PREF_CONFIG,
# or mdpi, in order of preference. We support both specific buckets (e.g. xdpi) and numbers,
# which get remapped to a bucket.
recovery_density := $(or $(TARGET_SCREEN_DENSITY),$(PRODUCT_AAPT_PREF_CONFIG),mdpi)
ifeq (,$(filter xxxhdpi xxhdpi xhdpi hdpi mdpi,$(recovery_density)))
recovery_density_value := $(patsubst %dpi,%,$(recovery_density))
# We roughly use the medium point between the primary densities to split buckets.
# ------160------240------320----------480------------640------
#       mdpi     hdpi    xhdpi        xxhdpi        xxxhdpi
recovery_density := $(strip \
  $(or $(if $(filter $(shell echo $$(($(recovery_density_value) >= 560))),1),xxxhdpi),\
       $(if $(filter $(shell echo $$(($(recovery_density_value) >= 400))),1),xxhdpi),\
       $(if $(filter $(shell echo $$(($(recovery_density_value) >= 280))),1),xhdpi),\
       $(if $(filter $(shell echo $$(($(recovery_density_value) >= 200))),1),hdpi,mdpi)))
endif

ifneq (,$(wildcard $(recovery_resources_common)-$(recovery_density)))
recovery_resources_common := $(recovery_resources_common)-$(recovery_density)
else
recovery_resources_common := $(recovery_resources_common)-xhdpi
endif

# Select the 18x32 font on high-density devices (xhdpi and up); and the 12x22 font on other devices.
# Note that the font selected here can be overridden for a particular device by putting a font.png
# in its private recovery resources.
ifneq (,$(filter xxxhdpi xxhdpi xhdpi,$(recovery_density)))
recovery_font := $(call include-path-for, recovery)/fonts/18x32.png
else
recovery_font := $(call include-path-for, recovery)/fonts/12x22.png
endif


# We will only generate the recovery background text images if the variable
# TARGET_RECOVERY_UI_SCREEN_WIDTH is defined. For devices with xxxhdpi and xxhdpi, we set the
# variable to the commonly used values here, if it hasn't been intialized elsewhere. While for
# devices with lower density, they must have TARGET_RECOVERY_UI_SCREEN_WIDTH defined in their
# BoardConfig in order to use this feature.
ifeq ($(recovery_density),xxxhdpi)
TARGET_RECOVERY_UI_SCREEN_WIDTH ?= 1440
else ifeq ($(recovery_density),xxhdpi)
TARGET_RECOVERY_UI_SCREEN_WIDTH ?= 1080
else ifeq ($(recovery_density),xhdpi)
TARGET_RECOVERY_UI_SCREEN_WIDTH ?= 720
else ifeq ($(recovery_density),hdpi)
TARGET_RECOVERY_UI_SCREEN_WIDTH ?= 480
endif

ifneq ($(TARGET_RECOVERY_UI_SCREEN_WIDTH),)
# Subtracts the margin width and menu indent from the screen width; it's safe to be conservative.
ifeq ($(TARGET_RECOVERY_UI_MARGIN_WIDTH),)
  recovery_image_width := $$(($(TARGET_RECOVERY_UI_SCREEN_WIDTH) - 10))
else
  recovery_image_width := $$(($(TARGET_RECOVERY_UI_SCREEN_WIDTH) - $(TARGET_RECOVERY_UI_MARGIN_WIDTH) - 10))
endif


RECOVERY_INSTALLING_TEXT_FILE := $(call intermediates-dir-for,PACKAGING,recovery_text_res)/installing_text.png
RECOVERY_INSTALLING_SECURITY_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/installing_security_text.png
RECOVERY_ERASING_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/erasing_text.png
RECOVERY_ERROR_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/error_text.png
RECOVERY_NO_COMMAND_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/no_command_text.png

RECOVERY_CANCEL_WIPE_DATA_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/cancel_wipe_data_text.png
RECOVERY_FACTORY_DATA_RESET_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/factory_data_reset_text.png
RECOVERY_TRY_AGAIN_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/try_again_text.png
RECOVERY_WIPE_DATA_CONFIRMATION_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/wipe_data_confirmation_text.png
RECOVERY_WIPE_DATA_MENU_HEADER_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/wipe_data_menu_header_text.png

generated_recovery_text_files := \
  $(RECOVERY_INSTALLING_TEXT_FILE) \
  $(RECOVERY_INSTALLING_SECURITY_TEXT_FILE) \
  $(RECOVERY_ERASING_TEXT_FILE) \
  $(RECOVERY_ERROR_TEXT_FILE) \
  $(RECOVERY_NO_COMMAND_TEXT_FILE) \
  $(RECOVERY_CANCEL_WIPE_DATA_TEXT_FILE) \
  $(RECOVERY_FACTORY_DATA_RESET_TEXT_FILE) \
  $(RECOVERY_TRY_AGAIN_TEXT_FILE) \
  $(RECOVERY_WIPE_DATA_CONFIRMATION_TEXT_FILE) \
  $(RECOVERY_WIPE_DATA_MENU_HEADER_TEXT_FILE)

resource_dir := $(call include-path-for, recovery)/tools/recovery_l10n/res/
image_generator_jar := $(HOST_OUT_JAVA_LIBRARIES)/RecoveryImageGenerator.jar
zopflipng := $(HOST_OUT_EXECUTABLES)/zopflipng
$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_SOURCE_FONTS := $(recovery_noto-fonts_dep) $(recovery_roboto-fonts_dep)
$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_FONT_FILES_DIR := $(call intermediates-dir-for,PACKAGING,recovery_font_files)
$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RESOURCE_DIR := $(resource_dir)
$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_IMAGE_GENERATOR_JAR := $(image_generator_jar)
$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_ZOPFLIPNG := $(zopflipng)
$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_IMAGE_WIDTH := $(recovery_image_width)
$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_BACKGROUND_TEXT_LIST := \
  recovery_installing \
  recovery_installing_security \
  recovery_erasing \
  recovery_error \
  recovery_no_command
$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_WIPE_DATA_TEXT_LIST := \
  recovery_cancel_wipe_data \
  recovery_factory_data_reset \
  recovery_try_again \
  recovery_wipe_data_menu_header \
  recovery_wipe_data_confirmation
$(RECOVERY_INSTALLING_TEXT_FILE): .KATI_IMPLICIT_OUTPUTS := $(filter-out $(RECOVERY_INSTALLING_TEXT_FILE),$(generated_recovery_text_files))
$(RECOVERY_INSTALLING_TEXT_FILE): $(image_generator_jar) $(resource_dir) $(recovery_noto-fonts_dep) $(recovery_roboto-fonts_dep) $(zopflipng)
	# Prepares the font directory.
	@rm -rf $(PRIVATE_RECOVERY_FONT_FILES_DIR)
	@mkdir -p $(PRIVATE_RECOVERY_FONT_FILES_DIR)
	$(foreach filename,$(PRIVATE_SOURCE_FONTS), cp $(filename) $(PRIVATE_RECOVERY_FONT_FILES_DIR) &&) true
	@rm -rf $(dir $@)
	@mkdir -p $(dir $@)
	$(foreach text_name,$(PRIVATE_RECOVERY_BACKGROUND_TEXT_LIST) $(PRIVATE_RECOVERY_WIPE_DATA_TEXT_LIST), \
	  $(eval output_file := $(dir $@)/$(patsubst recovery_%,%_text.png,$(text_name))) \
	  $(eval center_alignment := $(if $(filter $(text_name),$(PRIVATE_RECOVERY_BACKGROUND_TEXT_LIST)), --center_alignment)) \
	  java -jar $(PRIVATE_IMAGE_GENERATOR_JAR) \
	    --image_width $(PRIVATE_RECOVERY_IMAGE_WIDTH) \
	    --text_name $(text_name) \
	    --font_dir $(PRIVATE_RECOVERY_FONT_FILES_DIR) \
	    --resource_dir $(PRIVATE_RESOURCE_DIR) \
	    --output_file $(output_file) $(center_alignment) && \
	  $(PRIVATE_ZOPFLIPNG) -y --iterations=1 --filters=0 $(output_file) $(output_file) > /dev/null &&) true
else
RECOVERY_INSTALLING_TEXT_FILE :=
RECOVERY_INSTALLING_SECURITY_TEXT_FILE :=
RECOVERY_ERASING_TEXT_FILE :=
RECOVERY_ERROR_TEXT_FILE :=
RECOVERY_NO_COMMAND_TEXT_FILE :=
RECOVERY_CANCEL_WIPE_DATA_TEXT_FILE :=
RECOVERY_FACTORY_DATA_RESET_TEXT_FILE :=
RECOVERY_TRY_AGAIN_TEXT_FILE :=
RECOVERY_WIPE_DATA_CONFIRMATION_TEXT_FILE :=
RECOVERY_WIPE_DATA_MENU_HEADER_TEXT_FILE :=
endif # TARGET_RECOVERY_UI_SCREEN_WIDTH

代码很多,其中最关键代码如下,是通过 jar 包去生成 recovery 字体图片的

java -jar $(PRIVATE_IMAGE_GENERATOR_JAR) \
    --image_width $(PRIVATE_RECOVERY_IMAGE_WIDTH) \
    --text_name $(text_name) \
    --font_dir $(PRIVATE_RECOVERY_FONT_FILES_DIR) \
    --resource_dir $(PRIVATE_RESOURCE_DIR) \
    --output_file $(output_file) $(center_alignment)

第一个参数 PRIVATE_IMAGE_GENERATOR_JAR 指的是用于生成图片的jar包

PRIVATE_IMAGE_GENERATOR_JAR := $(image_generator_jar)

image_generator_jar := $(HOST_OUT_JAVA_LIBRARIES)/RecoveryImageGenerator.jar

在 out/host 下生成的 jar 包路径 out/host/linux-x86/framework/RecoveryImageGenerator.jar

查找 RecoveryImageGenerator 模块在源码中对应的路径
pathmod RecoveryImageGenerator

alps/bootable/recovery/tools/image_generator

这个目录下有一个 java 文件,bootable/recovery/tools/image_generator/ImageGenerator.java 关键的 main() 函数代码如下

public static void main(String[] args)
        throws NumberFormatException, IOException, FontFormatException,
                LocalizedStringNotFoundException {
    Options options = createOptions();
    CommandLine cmd;
    try {
        cmd = new GnuParser().parse(options, args);
    } catch (ParseException e) {
        System.err.println(e.getMessage());
        printUsage(options);
        return;
    }

    int imageWidth = Integer.parseUnsignedInt(cmd.getOptionValue("image_width"));

    if (cmd.hasOption("verbose")) {
        LOGGER.setLevel(Level.INFO);
    } else {
        LOGGER.setLevel(Level.WARNING);
    }

    ImageGenerator imageGenerator =
            new ImageGenerator(
                    imageWidth,
                    cmd.getOptionValue("text_name"),
                    DEFAULT_FONT_SIZE,
                    cmd.getOptionValue("font_dir"),
                    cmd.hasOption("center_alignment"));

    Set<String> localesSet = null;
    if (cmd.hasOption("locales")) {
        String[] localesList = cmd.getOptionValue("locales").split(",");
        localesSet = new HashSet<>(Arrays.asList(localesList));
        // Ensures that we have the default locale, all english translations are identical.
        localesSet.add("en-rAU");
    }
    Map<Locale, String> localizedStringMap =
            imageGenerator.readLocalizedStringFromXmls(cmd.getOptionValue("resource_dir"),
                    localesSet);
    imageGenerator.generateImage(localizedStringMap, cmd.getOptionValue("output_file"));
}

构造函数

public ImageGenerator(
        int initialImageWidth,
        String textName,
        float fontSize,
        String fontDirPath,
        boolean centerAlignment) {
    mImageWidth = initialImageWidth;
    mImageHeight = INITIAL_HEIGHT;
    mVerticalOffset = 0;

    // Initialize the canvas with the default height.
    mBufferedImage = new BufferedImage(mImageWidth, mImageHeight, BufferedImage.TYPE_BYTE_GRAY);

    mTextName = textName;
    mFontSize = fontSize;
    mFontDirPath = fontDirPath;
    mLoadedFontMap = new TreeMap<>();

    mCenterAlignment = centerAlignment;
}

其中几个参数
1.PRIVATE_RECOVERY_IMAGE_WIDTH 图片宽度
PRIVATE_RECOVERY_IMAGE_WIDTH := $(recovery_image_width)
recovery_image_width := $ ( ( (( (((TARGET_RECOVERY_UI_SCREEN_WIDTH) - 10))
TARGET_RECOVERY_UI_SCREEN_WIDTH ?= 720

2.text_name 是生成的recovery图片的名称,是从 $(PRIVATE_RECOVERY_BACKGROUND_TEXT_LIST)$(PRIVATE_RECOVERY_WIPE_DATA_TEXT_LIST)这两个集合变量中遍历而来的

$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_BACKGROUND_TEXT_LIST := \
  recovery_installing \
  recovery_installing_security \
  recovery_erasing \
  recovery_error \
  recovery_no_command
$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_WIPE_DATA_TEXT_LIST := \
  recovery_cancel_wipe_data \
  recovery_factory_data_reset \
  recovery_try_again \
  recovery_wipe_data_menu_header \
  recovery_wipe_data_confirmation

3.DEFAULT_FONT_SIZE = 40; 默认字体大小,单位是 px

4.PRIVATE_RECOVERY_FONT_FILES_DIR 字体文件目录
PRIVATE_RECOVERY_FONT_FILES_DIR := $(call intermediates-dir-for,PACKAGING,recovery_font_files)
该目录下的文件是从 PRIVATE_SOURCE_FONTS := $(recovery_noto-fonts_dep) $(recovery_roboto-fonts_dep) 里面拷过来的

经过搜索发现 recovery_noto-fonts_dep 字体文件在 external/noto-fonts
经过搜索发现 recovery_roboto-fonts_dep 字体文件在 external/roboto-fonts

5.centerAlignment 是否居中

6.PRIVATE_RESOURCE_DIR 字符串资源目录
PRIVATE_RESOURCE_DIR := $(resource_dir)
resource_dir := $(call include-path-for, recovery)/tools/recovery_l10n/res/

7.output_file 输出文件路径
$(eval output_file := $(dir @ ) / @)/ @)/(patsubst recovery_%,%_text.png,$(text_name)))

解析字符串代码,第一个参数是字符串资源文件的路径,第二个参数是 locales 语言集合,可以通过参数传递进来,这个方法里面最关键的方法是 getTextString(),这个方式里面是通过DOM解析xml字符串资源文件的

public Map<Locale, String> readLocalizedStringFromXmls(String resourcePath,
        Set<String> localesSet) throws IOException, LocalizedStringNotFoundException {
    File resourceDir = new File(resourcePath);
    if (!resourceDir.isDirectory()) {
        throw new LocalizedStringNotFoundException(resourcePath + " is not a directory.");
    }

    Map<Locale, String> result =
            // Overrides the string comparator so that sr is sorted behind sr-Latn. And thus
            // recovery can find the most relevant locale when going down the list.
            new TreeMap<>(
                    (Locale l1, Locale l2) -> {
                        if (l1.toLanguageTag().equals(l2.toLanguageTag())) {
                            return 0;
                        }
                        if (l1.getLanguage().equals(l2.toLanguageTag())) {
                            return -1;
                        }
                        if (l2.getLanguage().equals(l1.toLanguageTag())) {
                            return 1;
                        }
                        return l1.toLanguageTag().compareTo(l2.toLanguageTag());
                    });

    // Find all the localized resource subdirectories in the format of values-$LOCALE
    String[] nameList =
            resourceDir.list((File file, String name) -> name.startsWith("values-"));
    for (String name : nameList) {
        String localeString = name.substring(7);
        if (localesSet != null && !localesSet.contains(localeString)) {
            LOGGER.info("Skip parsing text for locale " + localeString);
            continue;
        }

        File textFile = new File(resourcePath, name + "/strings.xml");
        String localizedText;
        try {
            localizedText = getTextString(textFile, mTextName);
        } catch (IOException | ParserConfigurationException | org.xml.sax.SAXException e) {
            throw new LocalizedStringNotFoundException(
                    "Failed to read the translated text for locale " + name, e);
        }

        Locale locale = getLocaleFromFilename(name);
        // Removes the double quotation mark from the text.
        result.put(locale, localizedText.substring(1, localizedText.length() - 1));
    }

    return result;
}

最后的 imageGenerator.generateImage(localizedStringMap, cmd.getOptionValue(“output_file”)); 方法用来生成 recovery 图片资源文件

public void generateImage(Map<Locale, String> localizedTextMap, String outputPath)
        throws FontFormatException, IOException {
    FontMetrics defaultFontMetrics =
            createGraphics(Locale.forLanguageTag("en")).getFontMetrics();
    mDefaultFont = defaultFontMetrics.getFont();
    mAndroidStringWidth = defaultFontMetrics.stringWidth(ANDROID_STRING);

    // The last country variant should be the fallback locale for a given language.
    Map<String, Locale> fallbackLocaleMap = new HashMap<>();
    int textWidth = 0;
    for (Locale locale : localizedTextMap.keySet()) {
        // Updates the fallback locale if we have a new language variant. Don't do it for en-XC
        // as it's a pseudo-locale.
        if (!locale.toLanguageTag().equals("en-XC")) {
            fallbackLocaleMap.put(locale.getLanguage(), locale);
        }
        textWidth = Math.max(textWidth, measureTextWidth(localizedTextMap.get(locale), locale));
    }

    // Removes the black margins to reduce the size of the image.
    resize(textWidth, mImageHeight);

    for (Locale locale : localizedTextMap.keySet()) {
        // Recovery expects en-US instead of en_US.
        String languageTag = locale.toLanguageTag();
        Locale fallbackLocale = fallbackLocaleMap.get(locale.getLanguage());
        if (locale.equals(fallbackLocale)) {
            // Makes the last country variant for a given language be the catch-all for that
            // language.
            languageTag = locale.getLanguage();
        } else if (localizedTextMap.get(locale).equals(localizedTextMap.get(fallbackLocale))) {
            LOGGER.info("Skip parsing text for duplicate locale " + locale);
            continue;
        }

        drawText(localizedTextMap.get(locale), locale, languageTag);
    }

    resize(mImageWidth, mVerticalOffset);
    ImageIO.write(mBufferedImage, "png", new File(outputPath));
}

其中 resize()方法用来去除图片右边的黑色空白,所以一开始输入的图片宽度和最终生成图片宽度是有差异的

ImageIO.write()就是保存图片到指定路径的方法

总结:由此可知要修改字体的大小只需要修改 DEFAULT_FONT_SIZE的大小即可
添加修改字符串资源只需要修改 bootable/recovery/tools/recovery_l10n/res/目录下的字符串文件即可

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值