高通 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")
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章