高通 Splash Logo 镜像生成脚本解读

前言

因为需要做默认高通平台的 splash.img 为单张图片,需要修改支持多张,所以就看了下这块代码,贴出来备份下

源码

#===========================================================================

#  This script read the logo png and creates the logo.img

# when          who     what, where, why
# --------      ---     -------------------------------------------------------
# 2013-04       QRD     init
# 2015-04       QRD     support the RLE24 compression

# Environment requirement:
#     Python + PIL
#     PIL install:
#         (ubuntu)  sudo apt-get install python-imaging
#         (windows) (http://www.pythonware.com/products/pil/)

# limit:
#    a This script only support Python 2.7.x, 2.6.x,
#      Can't use in py3x for StringIO module
#    b This script's input can be a png, jpeg, bmp, gif file.
#    But if it is a gif, only get the first frame by default.
#
# description:
#    struct logo_header {
#       unsigned char[8]; // "SPLASH!!"
#       unsigned width;   // logo's width, little endian
#       unsigned height;  // logo's height, little endian
#       unsigned type;    // 0, Raw Image; 1, RLE24 Compressed Image
#       unsigned blocks;  // block number, real size / 512
#       ......
#    };

#    the logo Image layout:
#       logo_header + Payload data

# ===========================================================================*/

"""
一张 10x10 全红图片生成的 splash.img 

    00000000h: 53 50 4C 41 53 48 21 21 0A 00 00 00 0A 00 00 00 ; SPLASH!!........
    00000010h: 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000020h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000030h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000040h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000050h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000060h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000070h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000080h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000090h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    000000a0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    000000b0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    000000c0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    000000d0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    000000e0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    000000f0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000100h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000110h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000120h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000130h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000140h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000150h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000160h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000170h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000180h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000190h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    000001a0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    000001b0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    000001c0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    000001d0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    000001e0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    000001f0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    00000200h: 89 00 00 FF 89 00 00 FF 89 00 00 FF 89 00 00 FF ; ?.?.?.?.
    00000210h: 89 00 00 FF 89 00 00 FF 89 00 00 FF 89 00 00 FF ; ?.?.?.?.
    00000220h: 89 00 00 FF 89 00 00 FF                         ; ?.?.
    
    89: 137 - 127 = 10  压缩是以行为单位的
    00 00 FF: 红色
    
"""

from __future__ import print_function
import sys,os
import struct
import StringIO
from PIL import Image

SUPPORT_RLE24_COMPRESSIONT = 1
 
## get header
## 保存文件头
##   参数:  size: 文件大小
#           compressed: 是否压缩 
#           real_bytes: 压缩后实际文件大小
def GetImgHeader(size, compressed=0, real_bytes=0):
    SECTOR_SIZE_IN_BYTES = 512   # Header size
    header = [0 for i in range(SECTOR_SIZE_IN_BYTES)]

    width, height = size
    real_size = (real_bytes  + 511) / 512

    # magic
    header[:8] = [ord('S'),ord('P'), ord('L'), ord('A'),
                   ord('S'),ord('H'), ord('!'), ord('!')]

    # width
    header[8] = ( width        & 0xFF)
    header[9] = ((width >> 8 ) & 0xFF)
    header[10]= ((width >> 16) & 0xFF)
    header[11]= ((width >> 24) & 0xFF)

    # height
    header[12]= ( height        & 0xFF)
    header[13]= ((height >>  8) & 0xFF)
    header[14]= ((height >> 16) & 0xFF)
    header[15]= ((height >> 24) & 0xFF)

    #type
    header[16]= ( compressed    & 0xFF)
    #header[17]= 0
    #header[18]= 0
    #header[19]= 0

    # block number
    header[20] = ( real_size        & 0xFF)
    header[21] = ((real_size >>  8) & 0xFF)
    header[22] = ((real_size >> 16) & 0xFF)
    header[23] = ((real_size >> 24) & 0xFF)

    output = StringIO.StringIO()
    for i in header:
        output.write(struct.pack("B", i))
    content = output.getvalue()
    output.close()
    return content

########################################################################
# RLE 压缩实现核心,以行为单位进行压缩的
# 压缩算法:
#   相邻几个单像素,保存成如下格式:
#           [单像素总数(从0计数), 各颜色值]
#   相邻多个像素一样,保存成如下格式:
#           [127+像素数,颜色值]
#   相邻多个像素超过 128 个,则需要分成多个颜色组保存:
#             [256,颜色值] [256,颜色值] ... [127+像素数,颜色值]
#########################################################################    
def encode(line):
    # 相同像素重复次数计数
    count = 0
    lst = []
    # 上一个像素与当前像素是否重复
    repeat = -1
    run = []
    total = len(line) - 1
    
    # enumerate 返回一个带索引的列表, 用法如下:
    #   >>> seasons = ['Spring', 'Summer', 'Fall', 'Winter']
    #   >>> list(enumerate(seasons))
    #   [(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
    #   >>> list(enumerate(seasons, start=1))
    #   [(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]
    for index, current in enumerate(line[:-1]):
        
        # 如果当前值 =/= 下一个像素值
        if current != line[index + 1]:
            # run 中保存当前值 
            run.append(current)
            # 计数器加 1
            count += 1
            # 如果上一个字符与当前一样
            if repeat == 1:
                entry = (count+128,run)
                lst.append(entry)
                count = 0
                run = []
                repeat = -1
                # 遍历到最后一个字符,需要特殊处理
                if index == total - 1:
                    run = [line[index + 1]]
                    entry = (1,run)
                    lst.append(entry)
            # 如果上一个字符与当前不一样
            else:
                repeat = 0
                
                if count == 128:
                    entry = (128,run)
                    lst.append(entry)
                    count = 0
                    run = []
                    repeat = -1
                if index == total - 1:
                    run.append(line[index + 1])
                    entry = (count+1,run)
                    lst.append(entry)
                    
        # 如果当前值 == 下一个要遍历的像素值
        else:
            # 如果是首次累加,则保存到 1st 中
            if repeat == 0:
                entry = (count,run)
                lst.append(entry)
                count = 0
                run = []
                repeat = -1
                # 遍历到最后一个字符,需要特殊处理
                if index == total - 1:
                    run.append( line[index + 1])
                    run.append( line[index + 1])
                    entry = (2+128,run)
                    lst.append(entry)
                    break
            
            run.append(current)
            repeat = 1
            count += 1
            # 重复了 128 次?则保存一个 256 再次重新循环保存
            # 主要是因为计数从 127 开始,最大只能后 256
            if count == 128:
                entry = (256,run)
                lst.append(entry)
                count = 0
                run = []
                repeat = -1
            # 如果遍历后最后一次字符前
            if index == total - 1:
                if count == 0:
                    run = [line[index + 1]]
                    entry = (1,run)
                    lst.append(entry)
                else:
                    run.append(current)
                    entry = (count+1+128,run)
                    lst.append(entry)
    return lst

# 对 RGB 图像进行 RLE 压缩 
# 压缩算法原理如下:
# 行程编码:
#   原字符串:11111TTTUUUUUUTTTREABCDTTTTT
#   压缩字符:1#5#TTTU#6#TTTREABCDT#5#
#   解压字符:11111TTTUUUUUUTTTREABCDTTTTT
def encodeRLE24(img):
    width, height = img.size
    output = StringIO.StringIO()

    for h in range(height):
        # line 原图像数据列表 
        line = []
        # 压缩后的图像数据列表
        result=[]
        for w in range(width):
            # 返回给定位置的像素值,如果图像是一个多通道,则返回一个元组
            (r, g, b) = img.getpixel((w,h))
            # 添加到列表中
            line.append((r << 16)+(g << 8) + b)
            
        # 对每一行的数据进行压缩操作,
        #   他会将每个像素都变成 count:piexl 的格式?
        #   不然后面如何循环
        result = encode(line)
        
        # 
        for count, pixel in result:
            # struct.pack(fmt,v1,v2…) 按照给定的格式(fmt),把数据转换成字符串(字节流),并将该字符串返回.
            #   B: unsigned char 
            
            # 先写 count 值 
            output.write(struct.pack("B", count-1))
            
            # 再保存 piexl 值 
            if count > 128:
                output.write(struct.pack("B", (pixel[0]) & 0xFF))
                output.write(struct.pack("B", ((pixel[0]) >> 8) & 0xFF))
                output.write(struct.pack("B", ((pixel[0]) >> 16) & 0xFF))
            else:
                for item in pixel:
                    output.write(struct.pack("B", (item) & 0xFF))
                    output.write(struct.pack("B", (item >> 8) & 0xFF))
                    output.write(struct.pack("B", (item >> 16) & 0xFF))
    content = output.getvalue()
    output.close()
    return content


## get payload data : BGR Interleaved
def GetImageBody(img, compressed=0):
    # 定义了一个 元组
    color = (0, 0, 0)
    
    ##################################################
    # 判断图像的模式:定义的图像的类型和像素的位宽
    ##################################################
    if img.mode == "RGB":
    # RGB 颜色直接使用 
        background = img
    elif img.mode == "RGBA":
    # RGBA 的话,则截取 A 保存在 RGB 中
        background = Image.new("RGB", img.size, color)
        img.load()
        background.paste(img, mask=img.split()[3]) # alpha channel
    elif img.mode == "P" or img.mode == "L":
    # P 或者 L 类型的,直接粘贴到 RGB 类型中 
        background = Image.new("RGB", img.size, color)
        img.load()
        background.paste(img)
        #background.save("splash.png")
    else:
        print ("sorry, can't support this format")
        sys.exit()

    ##############################
    # 图像压缩?
    if compressed == 1:
        return encodeRLE24(background)
    else:
        # 获取各通道上的元组
        r, g, b = background.split()
        
        # 将 RGB 颠倒为 BGR 再保存了
        return Image.merge("RGB",(b,g,r)).tostring()

        
#############################################        
## make a image
# 核心函数:
def MakeLogoImage(logo, out):
    # 打开图片 
    img = Image.open(logo)
    
    # 打开文件
    file = open(out, "wb")
    
    # 获取文件内容,注会使用 RLE 进行压缩
    body = GetImageBody(img, SUPPORT_RLE24_COMPRESSIONT)
    
    # 1. 先写文件头 
    file.write(GetImgHeader(img.size, SUPPORT_RLE24_COMPRESSIONT, len(body)))
    
    # 2. 再写图像内容
    file.write(body)
    
    # 关闭文件
    file.close()


## mian

def ShowUsage():
    print(" usage: python logo_gen.py [logo.png]")

# 获取 png 文件路径,这里对传入的路径参数进行检查,判断文件是否存在    
def GetPNGFile():
    infile = "logo.png" #default file name
    num = len(sys.argv)
    if num > 3:
        ShowUsage()
        sys.exit(); # error arg

    if num == 2:
        infile = sys.argv[1]

    if os.access(infile, os.R_OK) != True:
        ShowUsage()
        sys.exit(); # error file
    return infile

#####################################################
# 函数入口 
#####################################################    
if __name__ == "__main__":
    MakeLogoImage(GetPNGFile(), "splash.img")
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Splash.img是一种用于替换开机第一帧图像的镜像文件。可以通过两种方式来替换LK display(bootloader)logo。一种是利用平台自带的logo_gen.py生成splash.img镜像,然后使用fastboot重新刷写splash.img分区。另一种是利用三方软件将.png格式的图片转换为bootable/bootloader/lk/platform/msm_shared/include/splash.h头文件的buffer,并替换原有的logo。 在制作开机第一帧时,可以将提供的图片复制到基线名/device/qcom/common/display/logo/目录下,并在该目录执行命令python logo_gen.py 图片名,即可生成splash.img。然后将生成splash.img复制到基线名/device/qcom/项目名/目录下,覆盖原有的splash.img文件。 另外,在高通6115平台上,开机第一帧的图片位于modem测代码的目录BOOT.XF.4.1/boot_images/QcomPkg/Logo/LA/logo1.bmp。制作这个开机第一帧图片的规则是使用bmp格式,深度为8位(可以使用Windows画图工具将256色位图另存为8位深度的.bmp格式)。 具体的代码逻辑可以参考源码bootable/bootloader/lk/dev/fbcon/fbcon.c的相关部分。系统首先会尝试从.img格式的镜像获取开机第一帧图像,如果失败,则会通过splash.h头文件的数组获取图像。如果数组无效或者获取失败,系统会显示默认的fbimg图像。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [高通平台第一帧splash和Bootanimation修改](https://blog.csdn.net/weixin_42237018/article/details/99678412)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [Android 开机第一帧制作、开关机动画制作、壁纸添加](https://blog.csdn.net/Thatgriler/article/details/127107184)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值