天天看點

Skia引擎圖像處理技術;為何在Android中占重要地位?

作者:Android秃老師

概述

Skia 是一款用 C++ 開發的、性能彪悍的 2D 圖像繪制引擎,其前身是一個向量繪圖軟體。2005年被谷歌收購,Skia也成為了Android系統的2D渲染顯示核心。Skia 在圖形轉換、文字渲染、位圖渲染方面都表現卓越,并提供了開發者友好的 API。

Skia在Android中的地位

  • 規定2D繪制API
  • 規定圖像資料結構
  • 承擔編解碼排程和軟體渲染職責

Skia初探學習

Android Java 2D作圖主要通過JNI調用skia圖形庫完成。Canvas是個2D的概念,在skia中可以把這個canvas了解成系統提供給我們的一塊記憶體區域,但實際上它隻是一套繪圖API,真正的記憶體是下面的Bitmap,skia 提供一個bitmap對象。

在surface上繪制UI,通過lock來擷取surface對應的資料buffer,并當做skia bitmap的記憶體,unlockandpost來通知Surfaceflinger來輸出顯示。

SkBitmap用來設定像素。SkCanvas執行寫入操作。SkPaint相當于畫筆,用來設定顔色(color), 字型(typeface), 文字大小(textSize), 文字粗細(strokeWidth), 漸變(gradients, patterns)等。

int main(int argc, char **argv)
{
    // create a client to surfaceflinger  
    sp<SurfaceComposerClient> client = new SurfaceComposerClient();
    sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
                        ISurfaceComposer::eDisplayIdMain));

    DisplayInfo dinfo;  
    status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
    printf("w=%d,h=%d,xdpi=%f,ydpi=%f,fps=%f,ds=%f\n",
                dinfo.w, dinfo.h, dinfo.xdpi, dinfo.ydpi, dinfo.fps, dinfo.density);
     
    sp<SurfaceControl> surfaceControl = 
            client->createSurface(String8("debugview"),
            dinfo.w, dinfo.h, 
            PIXEL_FORMAT_RGBA_8888, 
            0/*ISurfaceComposerClient::eHidden*/);
     
    surfaceControl;
    
    SurfaceComposerClient::openGlobalTransaction();
    surfaceControl->setLayer(100000);
    surfaceControl->setPosition(20, 20);
    //surfaceControl->setAlpha(0.3f);
    //surfaceControl->setSize(800, 800);
    //surfaceControl->setSize(1500, 1000);
    //Rect rect(900,900,900,900);
    //Region region(rect);
    //surfaceControl->setTransparentRegionHint(region);
    SurfaceComposerClient::closeGlobalTransaction(); 
    surfaceControl->show();
    sp<Surface> surface = surfaceControl->getSurface();
     
    ANativeWindow_Buffer outBuffer;  
    //Surface::SurfaceoutBuffer outBuffer;
    //surface->lock(&outBuffer,NULL);//
    //ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
    //android_memset16((uint16_t*)outBuffer.bits, 0xF800, bpr*outBuffer.height);
    //surface->unlockAndPost();
    //sleep(1);  
     
    ssize_t bpr;
    SkBitmap bitmap;
    
    //SkImageInfo info = SkImageInfo::MakeN32Premul(512, 512)
    //SkImageInfo info = SkImageInfo::Make(512, 512, 
    //           kN32_SkColorType, 
    //           kOpaque_SkAlphaType);
    SkImageInfo info = SkImageInfo::Make(512, 512, 
                        kN32_SkColorType, 
                        kPremul_SkAlphaType);
        
    surface->lock(&outBuffer, NULL);
    bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
    bitmap.installPixels(info, outBuffer.bits, bpr);
    SkCanvas canvas(bitmap);
    canvas.clear(SK_ColorTRANSPARENT);
    SkPaint paint;
    paint.setTextSize(28);
    paint.setColor(SK_ColorBLACK);
    //canvas.drawColor(SK_ColorRED);
    const char *str = "aaaaaaaaaaaaaaaaaaE";
    canvas.drawText(str, strlen(str), 8, 28, paint);
    surface->unlockAndPost();
    sleep(3);

#if 0
    SurfaceComposerClient::openGlobalTransaction();
    surfaceControl->setLayer(100000);
    surfaceControl->setPosition(0, 0);
    SurfaceComposerClient::closeGlobalTransaction();
    surfaceControl->show();

    sp<Surface> surface = surfaceControl->getSurface();
     
    ANativeWindow_Buffer outBuffer;
    surface->lock(&outBuffer,NULL);
    ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
    android_memset16((uint16_t*)outBuffer.bits, 0xF800, bpr*outBuffer.height);
    surface->unlockAndPost();
    sleep(3);
     
    SurfaceComposerClient::openGlobalTransaction();
    surfaceControl->setSize(480, 272);
    surfaceControl->setPosition(100, 100);
    SurfaceComposerClient::closeGlobalTransaction();
    surfaceControl->show();
    FILE *fp = fopen("/tmp/rgb565.rgb","rb");
    if(fp != NULL){
        unsigned char *rgb565Data = new unsigned char[480*272*2];
        memset(rgb565Data,0x00,480*272*2);
        fread(rgb565Data,1,480*272*2,fp);
        surface->lock(&outBuffer,NULL);
        memcpy(outBuffer.bits,rgb565Data,480*272*2);
        delete[] rgb565Data;
        surface->unlockAndPost();
    }
    fclose(fp);
    sleep(3);

    SurfaceComposerClient::openGlobalTransaction();
    surfaceControl->setSize(320, 420);
    surfaceControl->setPosition(100, 100);
    SurfaceComposerClient::closeGlobalTransaction();
    surfaceControl->show();
    SkPaint paint;
    paint.setColor(SK_ColorBLUE);
    Rect rect(0, 0, 320, 240);
    Region dirtyRegion(rect);
    surface->lock(&outBuffer, &rect);
    bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
    SkBitmap bitmap;
    bitmap.setConfig(convertPixelFormat(outBuffer.format), 320, 240, bpr);
    bitmap.setPixels(outBuffer.bits);
    SkCanvas canvas;
    SkRegion clipReg;
    const Rect b(dirtyRegion.getBounds());
    clipReg.setRect(b.left, b.top, b.right, b.bottom);
    canvas.clipRegion(clipReg);
    canvas.drawARGB(0, 0xFF, 0x00, 0xFF);
    canvas.drawCircle(200, 200, 100, paint);
    bitmap.notifyPixelsChanged();
    surface->unlockAndPost();
    sleep(3);
     
    SkFILEStream stream("/tmp/test.jpg");
    SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
    if(codec){
        SkBitmap bmp;
        stream.rewind();
        codec->decode(&stream, &bmp, 
                SkBitmap::kRGB_565_Config, 
                SkImageDecoder::kDecodePixels_Mode);
        surface->lock(&outBuffer,NULL);
        bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
        bitmap.setConfig(convertPixelFormat(outBuffer.format), 320, 240, bpr);
        bitmap.setPixels(outBuffer.bits);
        canvas.drawBitmap(bmp, SkIntToScalar(200), SkIntToScalar(300));
        surface->unlockAndPost();
        sleep(3);
    }   
#endif

    return 0;
}            

設定字型

SkTypeface *font = SkTypeface::CreateFromFile("xxx.otf");
if(font){
    paint.setARGB(255, 255, 0, 0);
    paint.setTypeface(font);
    paint.setTextSize(25);
    canvas.drawText("abc", 3, 20, 20, paint);
}           

還可以使用SkTypeface::CreateFromName建立face,skia會周遊SK_FONT_FILE_PREFIX宏所訓示的目錄下所有*.ttf字庫檔案,并将其加載記憶體。

以上我們簡單的介紹了Flutter中的Skia引擎圖像處理技術;在Android開發中啊Skia技術是很重要的存在,對于Skia的技術學習是需要更加深入學習,有關更多Skia技術和Flutter學習進階。可以私信:“手冊”前往擷取《Flutter3.0學習手冊》;海量Android開發技術知識進階也可以私信“資料”!

Skia引擎圖像處理技術;為何在Android中占重要地位?
Skia引擎圖像處理技術;為何在Android中占重要地位?
Skia引擎圖像處理技術;為何在Android中占重要地位?

【私信:“手冊”擷取】Android核心技術進階手冊

小結

目前,Skia 已然是 Android 官方的圖像渲染引擎了,是以 Flutter Android SDK 無需内嵌 Skia 引擎就可以獲得天然的 Skia 支援;而對于 iOS 平台來說,由于 Skia 是跨平台的,是以它作為 Flutter iOS 渲染引擎被嵌入到 Flutter 的 iOS SDK 中,替代了 iOS 閉源的 Core Graphics/Core Animation/Core Text,這也正是 Flutter iOS SDK 打包的 App 包體積比 Android 要大一些的原因。

繼續閱讀