關(guān)于 Skia
Skia 是一個 Google 開源的 C++ 二維圖形庫,提供各種常用的API,并可在多種軟硬件平臺上運行。
谷歌 Chrome 瀏覽器短曾、Chrome OS、Android 系統(tǒng)赐劣、火狐瀏覽器嫉拐、火狐操作系統(tǒng)以及其它許多產(chǎn)品都使用它作為圖形引擎。
特別是近兩年開始大火的跨平臺 App 開發(fā)框架 Flutter魁兼,其圖形引擎也是基于 Skia婉徘。
Skia 在 Chrome 和 Android 上作為底層的圖形引擎,可以類比為 iOS 上的 Core Graphic / Core Animation 一層的框架咐汞,可能 Google 并沒有大力在上層推廣這個框架盖呼,所以前端、移動端開發(fā)者關(guān)注 Skia 并不多化撕。甚至一些做了多年 Android 開發(fā)的同事也不知道有這個東西几晤,也可能跟 Android 系統(tǒng)并沒有在 Java 層暴露 Skia 的 API 有關(guān)。
關(guān)于本文
Skia 的中英文資料比較少植阴,Skia 官方給的 iOS Demo 也是完全拋棄了 iOS 原生的 Window蟹瘾、Application、View 體系掠手,用 C++ 實現(xiàn)了一版。
在 iOS 里使用 Skia惨撇,目前除了 Flutter,還沒有發(fā)現(xiàn)其它案例府寒。iOS 內(nèi)置的 UI 框架已經(jīng)足夠好用魁衙,如果不是為了實現(xiàn)跨平臺的能力报腔,也不太用的到 Skia纵隔。
如何將 C++ 的 Skia 元素,渲染到 iOS 原生的 UIView 體系上,Skia 本身沒有提供解決方案芦圾,iOS 端就更沒有接口可以用了。所以想在 iOS 平臺跑一個 Skia 的 Demo還是比較麻煩的眯杏,我參考了 OpenGL 的繪制思路,這里實現(xiàn)了一個將 Skia 的視圖元素 Canvas 的 Bitmap,繪制到 iOS 原生 UIView 體系上的一個 Demo姜性。真正應(yīng)用時不會這樣用部念,可以借助 SDL 或 OpenGL ES 渲染到 Native 層妓湘。
補充: Flutter Engine 里 iOS 端 Flutter 的渲染也大致是這個流程榜贴。
FlutterViewController 上的 FlutterView 里妹田,繪制邏輯最終體現(xiàn)在drawRect:
方法里,將 Engine shell 通過 Skia API 繪制的 UI霜浴,通過 Skia Picture 取出 Bitmap data阴孟,轉(zhuǎn)為 CGImageRef翁狐,再通過 CGContextDrawImage 繪制到 UIView 上
以下是在 iOS 中加載 Skia 的一個 Demo
Demo
import Skia ,需要將當(dāng)前類名改為 .mm
#import "ViewController.h"
#import "SkCanvas.h"
#import "SkGraphics.h"
#import "SkSurface.h"
#import "SkString.h"
#import "SkRRect.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor lightGrayColor];
int width = 300;
int height = 500;
SkBitmap bitmap;
bitmap.setInfo(SkImageInfo::Make(width, height, kRGBA_8888_SkColorType, kOpaque_SkAlphaType, nullptr));
bitmap.allocPixels();
SkCanvas canvas(bitmap);
canvas.drawColor(SK_ColorWHITE);
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
paint.setAntiAlias(true);
paint.setStrokeWidth(4);
paint.setColor(0xffFE938C);
SkRect rect_sk = SkRect::MakeXYWH(10, 30, 100, 160);
canvas.drawRect(rect_sk, paint);
SkRRect oval;
oval.setOval(rect_sk);
oval.offset(40, 80);
paint.setColor(0xffE6B89C);
canvas.drawRRect(oval, paint);
paint.setColor(0xff9CAFB7);
canvas.drawCircle(180, 50, 25, paint);
rect_sk.offset(80, 50);
paint.setColor(0xff4281A4);
paint.setStyle(SkPaint::kStroke_Style);
canvas.drawRoundRect(rect_sk, 10, 10, paint);
SkPaint p;
p.setTextSize(20);
p.setStrokeWidth(2.0);
p.setAntiAlias(true);
p.setColor(SK_ColorBLACK);
const char* str = "hello world";
canvas.drawText(str, strlen(str), 50, 300, p);
void* data = bitmap.getPixels();
NSUInteger dataLength = width * height * 4;
CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, data, dataLength, NULL);
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGImageRef iref = CGImageCreate(width, height, 8, 32, width * 4, colorspace,
kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast,
ref, NULL, true, kCGRenderingIntentDefault);
UIGraphicsBeginImageContext(CGSizeMake(width, height));
CGContextRef cgcontext = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(cgcontext, 0, height);
CGContextScaleCTM(cgcontext, 1.0, -1.0);
CGContextSetBlendMode(cgcontext, kCGBlendModeCopy);
CGContextDrawImage(cgcontext, CGRectMake(0.0, 0.0, width, height), iref);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CFRelease(ref);
CFRelease(colorspace);
CGImageRelease(iref);
bitmap.reset();
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.frame = CGRectMake(0, 0, width, height);
[self.view addSubview:imageView];
}
@end
Demo 里僅編譯 x86_64 版本的靜態(tài)庫崎脉,編譯方法參考 Skia 官方文檔骆膝。
Skia in Chrome
可以在 Chrome 里阅签,體驗一下 Skia 的 API:
https://fiddle.skia.org/c/@shapes