你們好,我是微微笑的蝸牛,🐌。css
上一節咱們講了如何肯定節點佈局信息,輸出了佈局樹。今天,將介紹最後一個環節,繪製。內容很少,相對也比較好理解。html
附上前面幾節的連接,想了解的童靴能夠先看看。git
整個過程以下:github
繪製命令是個啥?它描述了你該如何繪製一個圖形,好比位置大小、顏色、形狀等等。web
這裏,咱們只處理背景色和邊框的繪製,文字暫不涉及。因此只需支持矩形繪製,知道矩形的位置大小、顏色就好。swift
繪製命令,定義爲枚舉類型。暫且只支持矩形,關聯顏色和區域。api
// 繪製命令,目前只支持顏色繪製
enum DisplayCommand {
case SolidColor(Color, Rect)
}
複製代碼
畫布,用來保存像素信息。數組
// 畫布
struct Canvas {
var width: Int
var height: Int
// 像素點,argb
var pixels: [Color]
}
複製代碼
背景的繪製命令比較好生成。從節點樣式表中取出背景色 background
,再計算出背景區域就好。markdown
背景區域包括「內容區+內邊距+邊框」。數據結構
代碼以下:
// 繪製背景
func renderBackground(list: inout [DisplayCommand], layoutBox: LayoutBox) {
// 獲取背景色
if let color = getColor(layoutBox: layoutBox, name: "background") {
// 背景包括 padding + border + content
let displayCommand = DisplayCommand.SolidColor(color, layoutBox.dimensions.borderBox())
list.append(displayCommand)
}
}
複製代碼
一樣,首先從樣式表中取出邊框顏色 border-color
。
另外,邊框分爲上下左右四個矩形區域,需分別計算出來。以下所示:
繪製命令的生成跟背景色差很少,只是注意下矩形區域的計算。
遞歸遍歷佈局樹,將每一個節點的繪製命令加入到數組,便可獲得總繪製命令列表。
// 生成整體繪製列表
func buildDisplayList(layoutRoot: LayoutBox) -> [DisplayCommand] {
var list: [DisplayCommand] = []
renderLayoutBox(list: &list, layoutBox: layoutRoot)
return list
}
func renderLayoutBox(list: inout [DisplayCommand], layoutBox: LayoutBox) {
// 繪製背景
renderBackground(list: &list, layoutBox: layoutBox)
// 繪製邊框
renderBorder(list: &list, layoutBox: layoutBox)
// 遍歷子節點,遞歸生成命令
for child in layoutBox.children {
renderLayoutBox(list: &list, layoutBox: child)
}
}
複製代碼
將繪製命令轉換爲像素信息。
繪製命令中包含顏色和區域,將該區域中每一個點的色值寫入像素數組就好。
// 生成像素點
mutating func genPixel(color: Color, rect: Rect) {
// 將 rect 範圍內的點填充爲 color,不能超過畫布大小
// clamp 主要是用於限制範圍
let x0 = Int(rect.x.clamp(min: 0, max: Float(self.width)))
let y0 = Int(rect.y.clamp(min: 0, max: Float(self.height)))
let x1 = Int((rect.x + rect.width).clamp(min: 0, max: Float(self.width)))
let y1 = Int((rect.y + rect.height).clamp(min: 0, max: Float(self.height)))
// 遍歷全部點,橫向一行行填充
for y in y0...y1 {
for x in x0...x1 {
let index = y * width + x
pixels[index] = color
}
}
}
複製代碼
獲得像素信息數組後,使用 CoreGraphics 的 api 生成圖片。注意 color 的順序是 argb
。
func imageFromARGB32Bitmap(pixels: [Color], width: Int, height: Int) -> UIImage? {
guard width > 0 && height > 0 else { return nil }
guard pixels.count == width * height else { return nil }
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)
let bitsPerComponent = 8
let bitsPerPixel = 32
var data = pixels // Copy to mutable []
guard let providerRef = CGDataProvider(data: NSData(bytes: &data,
length: data.count * MemoryLayout<PixelData>.size)
)
else { return nil }
guard let cgim = CGImage(
width: width,
height: height,
bitsPerComponent: bitsPerComponent,
bitsPerPixel: bitsPerPixel,
bytesPerRow: width * MemoryLayout<PixelData>.size,
space: rgbColorSpace,
bitmapInfo: bitmapInfo,
provider: providerRef,
decode: nil,
shouldInterpolate: true,
intent: .defaultIntent
)
else { return nil }
return UIImage(cgImage: cgim)
}
複製代碼
這步只需建立一個 ImageView 將 image 顯示出來。
測試數據在工程目錄下的 Example 下面,test.html 和 test.css。
效果以下圖所示:
完整代碼可點此查看:github.com/silan-liu/t…。
這一節主要介紹了繪製的相關操做,重點在於將佈局信息生成繪製列表,而後進行光柵化,轉換爲像素點的過程。
至此,「據說你想寫個渲染引擎」系列已所有完結,感謝您的閱讀~