在 iOS 中使用 Skia

關(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

skia demo
#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 官方文檔骆膝。

Demo 地址

Skia in Chrome

可以在 Chrome 里阅签,體驗一下 Skia 的 API:
https://fiddle.skia.org/c/@shapes

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市养交,隨后出現(xiàn)的幾起案子衷戈,更是在濱河造成了極大的恐慌,老刑警劉巖层坠,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異刁笙,居然都是意外死亡破花,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門疲吸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來座每,“玉大人,你說我怎么就攤上這事摘悴∏褪幔” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵蹂喻,是天一觀的道長葱椭。 經(jīng)常有香客問我,道長口四,這世上最難降的妖魔是什么孵运? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蔓彩,結(jié)果婚禮上治笨,老公的妹妹穿的比我還像新娘。我一直安慰自己赤嚼,他們只是感情好旷赖,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著更卒,像睡著了一般等孵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上逞壁,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天流济,我揣著相機與錄音,去河邊找鬼腌闯。 笑死绳瘟,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的姿骏。 我是一名探鬼主播糖声,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蘸泻?” 一聲冷哼從身側(cè)響起琉苇,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎悦施,沒想到半個月后并扇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡抡诞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年穷蛹,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昼汗。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡肴熏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出顷窒,到底是詐尸還是另有隱情蛙吏,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布鞋吉,位于F島的核電站鸦做,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏坯辩。R本人自食惡果不足惜馁龟,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望漆魔。 院中可真熱鬧坷檩,春花似錦、人聲如沸改抡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽阿纤。三九已至句灌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間欠拾,已是汗流浹背胰锌。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留藐窄,地道東北人资昧。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像荆忍,于是被迫代替她去往敵國和親格带。 傳聞我的和親對象是個殘疾皇子撤缴,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344